This commit is contained in:
2025-08-20 13:27:51 +08:00
parent eaf2f54569
commit 617f8da51e
15 changed files with 1295 additions and 61 deletions

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<FleckDef ParentName="FleckBase">
<defName>WULA_GunTail_Blue</defName>
<altitudeLayer>Projectile</altitudeLayer>
<fadeOutTime>0.5</fadeOutTime>
<graphicData>
<texPath>Things/Projectile/ChargeLanceShot</texPath>
<shaderType>MoteGlow</shaderType>
<drawSize>(0.45,2)</drawSize>
</graphicData>
</FleckDef>
<FleckDef ParentName="FleckBase">
<defName>WULA_GunTail_Smoke</defName>
<altitudeLayer>Projectile</altitudeLayer>
<fadeOutTime>0.23</fadeOutTime>
<graphicData>
<texPath>Things/Others/Smoke_Dark</texPath>
<shaderType>TransparentPostLight</shaderType>
<drawSize>(0.06,0.25)</drawSize>
</graphicData>
</FleckDef>
</Defs>

View File

@@ -1725,7 +1725,6 @@
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>Bullet_WULA_WM_Panzer_Turret</defaultProjectile>
<warmupTime>0</warmupTime>
<forcedMissRadius>0.1</forcedMissRadius>
<minRange>5.9</minRange>
<range>75</range>
<burstShotCount>1</burstShotCount>

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>Weapon_ExampleCruiseMissileLauncher</defName>
<label>Cruise Missile Launcher</label>
<description>A heavy launcher designed for cruise missiles, capable of long-range precision strikes and area bombardment.</description>
<techLevel>Spacer</techLevel>
<graphicData>
<texPath>Things/Item/Weapon/Launcher</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<generateCommonality>1</generateCommonality>
<soundInteract>Interact_ChargeRifle</soundInteract> <!-- Placeholder, needs a proper sound -->
<weaponClasses>
<li>LongShots</li>
<li>RangedHeavy</li>
</weaponClasses>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<statBases>
<WorkToMake>2500</WorkToMake>
<Mass>6.0</Mass>
<AccuracyTouch>0.3</AccuracyTouch>
<AccuracyShort>0.5</AccuracyShort>
<AccuracyMedium>0.6</AccuracyMedium>
<AccuracyLong>0.7</AccuracyLong>
<RangedWeapon_Cooldown>4.0</RangedWeapon_Cooldown>
</statBases>
<costList Inherit="False">
<Steel>180</Steel>
<ComponentIndustrial>10</ComponentIndustrial>
<Plasteel>40</Plasteel>
<Uranium>15</Uranium>
</costList>
<verbs>
<li>
<verbClass>Verb_LaunchProjectile</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>Bullet_ExampleCruiseMissile</defaultProjectile>
<warmupTime>3.0</warmupTime>
<range>60</range>
<burstShotCount>1</burstShotCount>
<soundCast>Shot_ChargeLance</soundCast> <!-- Placeholder, needs a proper sound -->
<soundCastTail>GunTail_Heavy</soundCastTail>
<muzzleFlashScale>15</muzzleFlashScale>
<targetParams>
<canTargetLocations>true</canTargetLocations>
</targetParams>
<forcedMiss>false</forcedMiss>
</li>
</verbs>
<weaponTags>
<li>Wula_Weapon_Init</li>
<li>CruiseMissileLauncher</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
</ThingDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_ExampleCruiseMissile</defName>
<label>cruise missile</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Big</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<thingClass>WulaFallenEmpire.Projectile_CruiseMissile</thingClass>
<projectile>
<speed>30</speed>
<damageDef>Bomb</damageDef>
<damageAmountBase>20</damageAmountBase>
<stoppingPower>2.5</stoppingPower>
<armorPenetrationBase>0.8</armorPenetrationBase>
<soundExplode>Explosion_Bomb</soundExplode>
<soundImpactAnticipate>Impact_Metal</soundImpactAnticipate>
<flyOverhead>true</flyOverhead>
<shadowSize>1.0</shadowSize>
<explosionRadius>4.0</explosionRadius>
<postExplosionSpawnThingDef>Filth_Rubble</postExplosionSpawnThingDef>
<postExplosionSpawnChance>1.0</postExplosionSpawnChance>
<postExplosionSpawnThingCount>5</postExplosionSpawnThingCount>
<explosionDelay>120</explosionDelay>
<screenShakeFactor>1.5</screenShakeFactor>
<applyDamageToExplosionCellsNeighbors>true</applyDamageToExplosionCellsNeighbors>
<preExplosionSpawnChance>1.0</preExplosionSpawnChance>
<preExplosionSpawnThingCount>8</preExplosionSpawnThingCount>
<explosionChanceToStartFire>0.7</explosionChanceToStartFire>
<explosionDamageFalloff>true</explosionDamageFalloff>
<doExplosionVFX>true</doExplosionVFX>
<modExtensions>
<li Class="WulaFallenEmpire.CruiseMissileProperties">
<customDamageDef>Bomb</customDamageDef>
<customDamageAmount>25</customDamageAmount>
<customExplosionRadius>3.5</customExplosionRadius>
<customSoundExplode>Explosion_Bomb</customSoundExplode>
<useSubExplosions>true</useSubExplosions>
<subExplosionCount>5</subExplosionCount>
<subExplosionRadius>2.0</subExplosionRadius>
<subExplosionDamage>15</subExplosionDamage>
<subExplosionSpread>7.0</subExplosionSpread>
<subDamageDef>Bomb</subDamageDef>
<subSoundExplode>Explosion_Bomb</subSoundExplode>
<tailFleckDef>WULA_GunTail_Smoke</tailFleckDef>
<homingSpeed>0.05</homingSpeed>
<initRotateAngle>15</initRotateAngle>
<destroyTicksAfterLosingTrack>
<min>180</min>
<max>300</max>
</destroyTicksAfterLosingTrack>
<speedChangePerTick>0.001</speedChangePerTick>
<speedRangeOverride>
<min>10</min>
<max>20</max>
</speedRangeOverride>
<proximityFuseRange>1.5</proximityFuseRange>
</li>
</modExtensions>
</projectile>
</ThingDef>
</Defs>

View File

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>Weapon_ExampleHomingGun</defName>
<label>Homing Rifle</label>
<description>A rifle designed to fire homing projectiles. It can track targets and adjust its trajectory in real-time.</description>
<techLevel>Spacer</techLevel>
<graphicData>
<texPath>Things/Item/Weapon/Rifle</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<generateCommonality>1</generateCommonality>
<soundInteract>Interact_Rifle</soundInteract>
<weaponClasses>
<li>LongShots</li>
<li>RangedHeavy</li>
</weaponClasses>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<statBases>
<WorkToMake>1500</WorkToMake>
<Mass>3.0</Mass>
<AccuracyTouch>0.6</AccuracyTouch>
<AccuracyShort>0.75</AccuracyShort>
<AccuracyMedium>0.85</AccuracyMedium>
<AccuracyLong>0.8</AccuracyLong>
<RangedWeapon_Cooldown>1.2</RangedWeapon_Cooldown>
</statBases>
<costList Inherit="False">
<Steel>100</Steel>
<ComponentIndustrial>5</ComponentIndustrial>
<Plasteel>20</Plasteel>
</costList>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>Bullet_ExampleHoming</defaultProjectile>
<warmupTime>1.0</warmupTime>
<range>35</range>
<burstShotCount>1</burstShotCount>
<soundCast>Shot_ChargeRifle</soundCast> <!-- Placeholder, needs a proper sound -->
<soundCastTail>GunTail_Medium</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>
</li>
</verbs>
<weaponTags>
<li>Wula_Weapon_Init</li>
<li>HomingWeapon</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
</ThingDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_ExampleHoming</defName>
<label>homing bullet</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Big</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<thingClass>WulaFallenEmpire.Projectile_Homing</thingClass>
<projectile>
<speed>30</speed>
<damageDef>Bullet</damageDef>
<damageAmountBase>15</damageAmountBase>
<stoppingPower>1.5</stoppingPower>
<armorPenetrationBase>0.5</armorPenetrationBase>
<soundExplode>Explosion_Blasting</soundExplode>
<soundImpactAnticipate>Impact_Metal</soundImpactAnticipate>
<flyOverhead>false</flyOverhead>
<shadowSize>0.5</shadowSize>
<explosionRadius>0.5</explosionRadius>
<postExplosionSpawnThingDef>Filth_Rubble</postExplosionSpawnThingDef>
<postExplosionSpawnChance>0.5</postExplosionSpawnChance>
<postExplosionSpawnThingCount>1</postExplosionSpawnThingCount>
<explosionDelay>0</explosionDelay>
<screenShakeFactor>0.5</screenShakeFactor>
<applyDamageToExplosionCellsNeighbors>false</applyDamageToExplosionCellsNeighbors>
<preExplosionSpawnChance>0.5</preExplosionSpawnChance>
<preExplosionSpawnThingCount>1</preExplosionSpawnThingCount>
<explosionChanceToStartFire>0.1</explosionChanceToStartFire>
<explosionDamageFalloff>true</explosionDamageFalloff>
<doExplosionVFX>true</doExplosionVFX>
<modExtensions>
<li Class="WulaFallenEmpire.HomingProjectileDef">
<initRotateAngle>10</initRotateAngle>
<homingSpeed>0.05</homingSpeed>
<destroyTicksAfterLosingTrack>60</destroyTicksAfterLosingTrack>
<proximityFuseRange>1.5</proximityFuseRange>
<hitChance>0.8</hitChance>
<extraProjectile>Bullet_ExampleHoming</extraProjectile>
<SpeedChangeTilesPerTickOverride>0.05</SpeedChangeTilesPerTickOverride>
<SpeedRangeTilesPerTickOverride>
<min>20</min>
<max>40</max>
</SpeedRangeTilesPerTickOverride>
<tailFleckDef>WULA_GunTail_Smoke</tailFleckDef>
</li>
</modExtensions>
</projectile>
</ThingDef>
</Defs>

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>Weapon_ExampleHomingExplosiveLauncher</defName>
<label>Homing Explosive Launcher</label>
<description>A powerful launcher that fires homing projectiles with explosive payloads. Ideal for area denial and heavy damage.</description>
<techLevel>Spacer</techLevel>
<graphicData>
<texPath>Things/Item/Weapon/Launcher</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<generateCommonality>0.8</generateCommonality>
<soundInteract>Interact_ChargeRifle</soundInteract> <!-- Placeholder, needs a proper sound -->
<weaponClasses>
<li>LongShots</li>
<li>RangedHeavy</li>
</weaponClasses>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
</recipeMaker>
<statBases>
<WorkToMake>2000</WorkToMake>
<Mass>5.0</Mass>
<AccuracyTouch>0.4</AccuracyTouch>
<AccuracyShort>0.6</AccuracyShort>
<AccuracyMedium>0.7</AccuracyMedium>
<AccuracyLong>0.65</AccuracyLong>
<RangedWeapon_Cooldown>3.0</RangedWeapon_Cooldown>
</statBases>
<costList Inherit="False">
<Steel>150</Steel>
<ComponentIndustrial>8</ComponentIndustrial>
<Plasteel>30</Plasteel>
<Uranium>10</Uranium>
</costList>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>Bullet_ExampleHomingExplosive</defaultProjectile>
<warmupTime>2.5</warmupTime>
<range>40</range>
<burstShotCount>1</burstShotCount>
<soundCast>Shot_ChargeLance</soundCast> <!-- Placeholder, needs a proper sound -->
<soundCastTail>GunTail_Heavy</soundCastTail>
<muzzleFlashScale>12</muzzleFlashScale>
</li>
</verbs>
<weaponTags>
<li>Wula_Weapon_Init</li>
<li>HomingExplosiveWeapon</li>
</weaponTags>
<thingSetMakerTags>
<li>RewardStandardQualitySuper</li>
</thingSetMakerTags>
</ThingDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_ExampleHomingExplosive</defName>
<label>homing explosive bullet</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Big</texPath> <!-- Placeholder, needs a proper texture path -->
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<thingClass>WulaFallenEmpire.Projectile_Homing_Explosive</thingClass>
<projectile>
<speed>25</speed>
<damageDef>Bomb</damageDef>
<damageAmountBase>30</damageAmountBase>
<stoppingPower>2.0</stoppingPower>
<armorPenetrationBase>0.7</armorPenetrationBase>
<soundExplode>Explosion_Bomb</soundExplode>
<soundImpactAnticipate>Impact_Metal</soundImpactAnticipate>
<flyOverhead>true</flyOverhead>
<shadowSize>0.8</shadowSize>
<explosionRadius>3.0</explosionRadius>
<postExplosionSpawnThingDef>Filth_Rubble</postExplosionSpawnThingDef>
<postExplosionSpawnChance>1.0</postExplosionSpawnChance>
<postExplosionSpawnThingCount>3</postExplosionSpawnThingCount>
<explosionDelay>60</explosionDelay>
<screenShakeFactor>1.0</screenShakeFactor>
<applyDamageToExplosionCellsNeighbors>true</applyDamageToExplosionCellsNeighbors>
<preExplosionSpawnChance>1.0</preExplosionSpawnChance>
<preExplosionSpawnThingCount>5</preExplosionSpawnThingCount>
<explosionChanceToStartFire>0.5</explosionChanceToStartFire>
<explosionDamageFalloff>true</explosionDamageFalloff>
<doExplosionVFX>true</doExplosionVFX>
<modExtensions>
<li Class="WulaFallenEmpire.HomingProjectileDef">
<initRotateAngle>5</initRotateAngle>
<homingSpeed>0.03</homingSpeed>
<destroyTicksAfterLosingTrack>90</destroyTicksAfterLosingTrack>
<proximityFuseRange>2.0</proximityFuseRange>
<hitChance>0.7</hitChance>
<extraProjectile>Bullet_ExampleHomingExplosive</extraProjectile>
<SpeedChangeTilesPerTickOverride>0.03</SpeedChangeTilesPerTickOverride>
<SpeedRangeTilesPerTickOverride>
<min>20</min>
<max>30</max>
</SpeedRangeTilesPerTickOverride>
<tailFleckDef>WULA_GunTail_Smoke</tailFleckDef>
</li>
<li Class="WulaFallenEmpire.ModExtension_Cone">
<coneAngle>30</coneAngle>
<coneRange>5</coneRange>
<repeatExplosionCount>3</repeatExplosionCount>
<fragment>Bullet_ShotgunPellet</fragment> <!-- Example fragment -->
<fragmentCount>5</fragmentCount>
<fragmentRange>
<min>1</min>
<max>3</max>
</fragmentRange>
<showConeEffect>true</showConeEffect>
</li>
</modExtensions>
</projectile>
</ThingDef>
</Defs>

View File

@@ -68,6 +68,7 @@
<maxHits>-1</maxHits> <!-- 无限穿透 -->
<damageFalloff>0</damageFalloff> <!-- 无伤害衰减 -->
<preventFriendlyFire>false</preventFriendlyFire> <!-- 是否阻止友方火力 -->
<tailFleckDef>WULA_GunTail_Blue</tailFleckDef> <!-- 拖尾特效定义 -->
</li>
</modExtensions>
<tickerType>Normal</tickerType>

View File

@@ -9,13 +9,7 @@
"path": "../../../../Data"
},
{
"path": "../../../3256974620"
},
{
"path": "../../../2953846705"
},
{
"path": "../../../1635901197"
"path": "../../../../../../workshop/content/294100/3534748687"
}
],
"settings": {}

View File

@@ -0,0 +1,41 @@
using System;
using Verse;
namespace WulaFallenEmpire
{
public class HomingProjectileDef : DefModExtension
{
public float SpeedChangeTilesPerTickOverride
{
get
{
return this.speedChangePerTick / 100f;
}
}
public FloatRange SpeedRangeTilesPerTickOverride
{
get
{
return this.speedRangeOverride.Value * 0.01f;
}
}
public float hitChance = 0.5f;
public float homingSpeed = 0.1f;
public float initRotateAngle = 30f;
public float proximityFuseRange = 0f;
public IntRange destroyTicksAfterLosingTrack = new IntRange(60, 120);
public ThingDef extraProjectile;
public float speedChangePerTick;
public FloatRange? speedRangeOverride;
public FleckDef tailFleckDef;
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class ModExtension_Cone : DefModExtension
{
public float coneAngle = 10f;
public float coneRange = 7f;
public int repeatExplosionCount = 1;
public ThingDef fragment;
public int fragmentCount;
public FloatRange? fragmentRange;
public bool showConeEffect = true;
public void DoConeExplosion(IntVec3 center, Map map, Quaternion rotation, DamageDef damType, Thing instigator, int damAmount = -1, float armorPenetration = -1f, SoundDef explosionSound = null, ThingDef weapon = null, ThingDef projectile = null, Thing intendedTarget = null, ThingDef postExplosionSpawnThingDef = null, float postExplosionSpawnChance = 0f, int postExplosionSpawnThingCount = 1, GasType? postExplosionGasType = null, float? postExplosionGasRadiusOverride = null, int postExplosionGasAmount = 255, bool applyDamageToExplosionCellsNeighbors = false, ThingDef preExplosionSpawnThingDef = null, float preExplosionSpawnChance = 0f, int preExplosionSpawnThingCount = 1, float chanceToStartFire = 0f, bool damageFalloff = false, float? direction = null, List<Thing> ignoredThings = null, float propagationSpeed = 1f, float excludeRadius = 0f, ThingDef postExplosionSpawnThingDefWater = null, float screenShakeFactor = 1f, SimpleCurve flammabilityChanceCurve = null, List<IntVec3> overrideCells = null)
{
Vector3 v = rotation * Vector3.forward;
FloatRange initialAngleRange = new FloatRange(v.ToAngleFlat() - coneAngle, v.ToAngleFlat() + coneAngle);
for (int i = 0; i < repeatExplosionCount; i++)
{
// Handle angle wrap-around for max > 360
if (initialAngleRange.max > 360f)
{
GenExplosion.DoExplosion(affectedAngle: new FloatRange(0f, initialAngleRange.max - 360f), center: center, map: map, radius: coneRange, damType: damType, instigator: instigator, damAmount: damAmount, armorPenetration: armorPenetration, explosionSound: explosionSound, weapon: weapon, projectile: projectile, intendedTarget: intendedTarget, postExplosionSpawnThingDef: postExplosionSpawnThingDef, postExplosionSpawnChance: postExplosionSpawnChance, postExplosionSpawnThingCount: postExplosionSpawnThingCount, postExplosionGasType: postExplosionGasType, postExplosionGasRadiusOverride: postExplosionGasRadiusOverride, postExplosionGasAmount: postExplosionGasAmount, applyDamageToExplosionCellsNeighbors: applyDamageToExplosionCellsNeighbors, preExplosionSpawnThingDef: preExplosionSpawnThingDef, preExplosionSpawnChance: preExplosionSpawnChance, preExplosionSpawnThingCount: preExplosionSpawnThingCount, chanceToStartFire: chanceToStartFire, damageFalloff: damageFalloff, direction: direction, ignoredThings: ignoredThings, doVisualEffects: showConeEffect, propagationSpeed: propagationSpeed, excludeRadius: excludeRadius, doSoundEffects: showConeEffect, postExplosionSpawnThingDefWater: postExplosionSpawnThingDefWater, screenShakeFactor: screenShakeFactor, flammabilityChanceCurve: flammabilityChanceCurve, overrideCells: overrideCells);
}
// Handle angle wrap-around for min < 0
if (initialAngleRange.min < 0f)
{
GenExplosion.DoExplosion(affectedAngle: new FloatRange(initialAngleRange.min + 360f, 360f), center: center, map: map, radius: coneRange, damType: damType, instigator: instigator, damAmount: damAmount, armorPenetration: armorPenetration, explosionSound: explosionSound, weapon: weapon, projectile: projectile, intendedTarget: intendedTarget, postExplosionSpawnThingDef: postExplosionSpawnThingDef, postExplosionSpawnChance: postExplosionSpawnChance, postExplosionSpawnThingCount: postExplosionSpawnThingCount, postExplosionGasType: postExplosionGasType, postExplosionGasRadiusOverride: postExplosionGasRadiusOverride, postExplosionGasAmount: postExplosionGasAmount, applyDamageToExplosionCellsNeighbors: applyDamageToExplosionCellsNeighbors, preExplosionSpawnThingDef: preExplosionSpawnThingDef, preExplosionSpawnChance: preExplosionSpawnChance, preExplosionSpawnThingCount: preExplosionSpawnThingCount, chanceToStartFire: chanceToStartFire, damageFalloff: damageFalloff, direction: direction, ignoredThings: ignoredThings, doVisualEffects: showConeEffect, propagationSpeed: propagationSpeed, excludeRadius: excludeRadius, doSoundEffects: showConeEffect, postExplosionSpawnThingDefWater: postExplosionSpawnThingDefWater, screenShakeFactor: screenShakeFactor, flammabilityChanceCurve: flammabilityChanceCurve, overrideCells: overrideCells);
}
// Main explosion
GenExplosion.DoExplosion(center, map, coneRange, damType, instigator, damAmount, armorPenetration, explosionSound, weapon, projectile, intendedTarget, postExplosionSpawnThingDef, postExplosionSpawnChance, postExplosionSpawnThingCount, postExplosionGasType, postExplosionGasRadiusOverride, postExplosionGasAmount, applyDamageToExplosionCellsNeighbors, preExplosionSpawnThingDef, preExplosionSpawnChance, preExplosionSpawnThingCount, chanceToStartFire, damageFalloff, direction, ignoredThings, initialAngleRange, showConeEffect, propagationSpeed, excludeRadius, showConeEffect, postExplosionSpawnThingDefWater, screenShakeFactor, flammabilityChanceCurve, overrideCells);
}
if (fragment != null)
{
FloatRange currentFragmentRange = fragmentRange.HasValue ? fragmentRange.Value : new FloatRange(0f, coneRange);
IEnumerable<IntVec3> source = FragmentCells(center, initialAngleRange, currentFragmentRange);
for (int j = 0; j < fragmentCount; j++)
{
IntVec3 intVec = source.RandomElement();
((Projectile)GenSpawn.Spawn(fragment, center, map)).Launch(instigator, intVec, intVec, ProjectileHitFlags.All);
}
}
}
private IEnumerable<IntVec3> FragmentCells(IntVec3 center, FloatRange? angle, FloatRange range)
{
int minRadialCells = GenRadial.NumCellsInRadius(range.min);
int maxRadialCells = GenRadial.NumCellsInRadius(range.max);
for (int i = minRadialCells; i < maxRadialCells; i++)
{
IntVec3 currentCell = center + GenRadial.RadialPattern[i];
if (angle.HasValue)
{
float angleMin = angle.Value.min;
float angleMax = angle.Value.max;
float lengthHorizontal = (currentCell - center).LengthHorizontal;
if (lengthHorizontal <= 0.5f) // Close to center, always include
{
yield return currentCell;
continue;
}
float cellAngle = Mathf.Atan2(-(currentCell.z - center.z), currentCell.x - center.x) * 57.29578f; // Convert radians to degrees
// Handle angle wrap-around for comparison
if (angleMin < 0f && cellAngle - angleMin > 360f)
{
cellAngle -= 360f;
}
if (angleMax > 360f && angleMax - cellAngle < 360f)
{
cellAngle += 360f;
}
// Check if cell is within the angular range
if (cellAngle >= angleMin && cellAngle <= angleMax)
{
yield return currentCell;
}
}
else
{
yield return currentCell; // No angle restriction
}
}
}
}
}

View File

@@ -0,0 +1,325 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
namespace WulaFallenEmpire
{
public class Projectile_Homing : Bullet
{
public HomingProjectileDef HomingDef
{
get
{
bool flag = this.homingDefInt == null;
if (flag)
{
this.homingDefInt = this.def.GetModExtension<HomingProjectileDef>();
}
return this.homingDefInt;
}
}
public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null)
{
bool flag = false;
bool flag2 = usedTarget.HasThing && usedTarget.Thing is IAttackTarget;
if (flag2)
{
bool flag3 = Rand.Chance(this.GetHitChance(usedTarget.Thing));
if (flag3)
{
hitFlags |= ProjectileHitFlags.IntendedTarget;
intendedTarget = usedTarget;
flag = true;
}
}
else
{
bool flag4 = Rand.Chance(this.GetHitChance(intendedTarget.Thing));
if (flag4)
{
hitFlags |= ProjectileHitFlags.IntendedTarget;
usedTarget = intendedTarget;
flag = true;
}
}
bool flag5 = flag;
if (flag5)
{
hitFlags &= ~ProjectileHitFlags.IntendedTarget;
}
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
this.exactPositionInt = origin.Yto0() + Vector3.up * this.def.Altitude;
Vector3 normalized = (this.destination - origin).Yto0().normalized;
float degrees = Rand.Range(-this.HomingDef.initRotateAngle, this.HomingDef.initRotateAngle);
Vector2 vector = new Vector2(normalized.x, normalized.z);
vector = vector.RotatedBy(degrees);
Vector3 a = new Vector3(vector.x, 0f, vector.y);
bool flag6 = this.HomingDef.speedRangeOverride == null;
if (flag6)
{
this.curSpeed = a * this.def.projectile.SpeedTilesPerTick;
}
else
{
this.curSpeed = a * this.HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange;
}
this.ticksToImpact = int.MaxValue;
this.lifetime = int.MaxValue;
this.ReflectInit();
}
protected void ReflectInit()
{
bool flag = !this.def.projectile.soundAmbient.NullOrUndefined();
if (flag)
{
this.ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this);
}
this.comps = (List<ThingComp>)NonPublicFields.ThingWithComps_comps.GetValue(this);
}
public float GetHitChance(Thing thing)
{
float num = this.HomingDef.hitChance;
bool flag = thing == null;
float result;
if (flag)
{
result = num;
}
else
{
Pawn pawn = thing as Pawn;
bool flag2 = pawn != null;
if (flag2)
{
num *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f);
bool flag3 = pawn.GetPosture() > PawnPosture.Standing;
if (flag3)
{
num *= 0.5f;
}
float num2 = 1f;
switch (this.equipmentQuality)
{
case QualityCategory.Awful:
num2 = 0.5f;
goto IL_DD;
case QualityCategory.Poor:
num2 = 0.75f;
goto IL_DD;
case QualityCategory.Normal:
num2 = 1f;
goto IL_DD;
case QualityCategory.Excellent:
num2 = 1.1f;
goto IL_DD;
case QualityCategory.Masterwork:
num2 = 1.2f;
goto IL_DD;
case QualityCategory.Legendary:
num2 = 1.3f;
goto IL_DD;
}
Log.Message("Unknown QualityCategory, returning default qualityFactor = 1");
IL_DD:
num *= num2;
}
else
{
num *= 1.5f * thing.def.fillPercent;
}
result = Mathf.Clamp(num, 0f, 1f);
}
return result;
}
public override Vector3 ExactPosition
{
get
{
return this.exactPositionInt;
}
}
public override Quaternion ExactRotation
{
get
{
return Quaternion.LookRotation(this.curSpeed);
}
}
public virtual void MovementTick()
{
Vector3 vect = this.ExactPosition + this.curSpeed;
ShootLine shootLine = new ShootLine(this.ExactPosition.ToIntVec3(), vect.ToIntVec3());
Vector3 vector = (this.intendedTarget.Cell.ToVector3() - this.ExactPosition).Yto0();
bool flag = this.homing;
if (flag)
{
Vector3 a = vector.normalized - this.curSpeed.normalized;
bool flag2 = a.sqrMagnitude >= 1.414f;
if (flag2)
{
this.homing = false;
this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
this.ticksToImpact = this.lifetime;
base.HitFlags &= ~ProjectileHitFlags.IntendedTarget;
base.HitFlags |= ProjectileHitFlags.NonTargetPawns;
base.HitFlags |= ProjectileHitFlags.NonTargetWorld;
}
else
{
this.curSpeed += a * this.HomingDef.homingSpeed * this.curSpeed.magnitude;
}
}
foreach (IntVec3 b in shootLine.Points())
{
bool flag3 = (this.intendedTarget.Cell - b).SqrMagnitude <= this.HomingDef.proximityFuseRange * this.HomingDef.proximityFuseRange;
if (flag3)
{
this.homing = false;
this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
bool flag4 = (base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || this.HomingDef.proximityFuseRange > 0f;
if (flag4)
{
this.lifetime = 0;
this.ticksToImpact = 0;
vect = b.ToVector3();
bool flag5 = Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null;
if (flag5)
{
this.def.projectile.soundImpactAnticipate.PlayOneShot(this);
}
}
}
}
this.exactPositionInt = vect;
this.curSpeed *= (this.curSpeed.magnitude + this.HomingDef.SpeedChangeTilesPerTickOverride) / this.curSpeed.magnitude;
}
protected override void Tick()
{
this.ThingWithCompsTick();
this.lifetime--;
if (this.HomingDef.tailFleckDef != null)
{
FleckMaker.Static(this.ExactPosition, base.Map, this.HomingDef.tailFleckDef, 1f);
}
bool landed = this.landed;
if (!landed)
{
Vector3 exactPosition = this.ExactPosition;
this.ticksToImpact--;
this.MovementTick();
bool flag = !this.ExactPosition.InBounds(base.Map);
if (flag)
{
base.Position = exactPosition.ToIntVec3();
this.Destroy(DestroyMode.Vanish);
}
else
{
Vector3 exactPosition2 = this.ExactPosition;
object[] parameters = new object[]
{
exactPosition,
exactPosition2
};
bool flag2 = (bool)Projectile_Homing.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters);
if (!flag2)
{
base.Position = this.ExactPosition.ToIntVec3();
bool flag3 = this.ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null;
if (flag3)
{
this.def.projectile.soundImpactAnticipate.PlayOneShot(this);
}
bool flag4 = this.ticksToImpact <= 0;
if (flag4)
{
this.ImpactSomething();
}
else
{
bool flag5 = this.ambientSustainer != null;
if (flag5)
{
this.ambientSustainer.Maintain();
}
}
}
}
}
}
private void ThingWithCompsTick()
{
bool flag = this.comps != null;
if (flag)
{
int i = 0;
int count = this.comps.Count;
while (i < count)
{
this.comps[i].CompTick();
i++;
}
}
}
protected override void Impact(Thing hitThing, bool blockedByShield = false)
{
Map map = base.Map;
IntVec3 position = base.Position;
base.Impact(hitThing, blockedByShield);
bool flag = this.HomingDef.extraProjectile != null;
if (flag)
{
bool flag2 = hitThing != null && hitThing.Spawned;
if (flag2)
{
((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null);
}
else
{
((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, position, position, ProjectileHitFlags.All, false, null, null);
}
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look<Vector3>(ref this.exactPositionInt, "exactPosition", default(Vector3), false);
Scribe_Values.Look<Vector3>(ref this.curSpeed, "curSpeed", default(Vector3), false);
Scribe_Values.Look<bool>(ref this.homing, "homing", false, false);
bool flag = Scribe.mode == LoadSaveMode.PostLoadInit;
if (flag)
{
this.ReflectInit();
}
}
private HomingProjectileDef homingDefInt;
private Sustainer ambientSustainer;
private List<ThingComp> comps;
protected Vector3 exactPositionInt;
public Vector3 curSpeed;
public bool homing = true;
private static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic);
}
}

View File

@@ -0,0 +1,142 @@
using System;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class Projectile_Homing_Explosive : Projectile_Homing
{
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look<int>(ref this.ticksToDetonation, "ticksToDetonation", 0, false);
}
protected override void Tick()
{
base.Tick();
bool flag = this.ticksToDetonation > 0;
if (flag)
{
this.ticksToDetonation--;
bool flag2 = this.ticksToDetonation <= 0;
if (flag2)
{
this.Explode();
}
}
}
protected override void Impact(Thing hitThing, bool blockedByShield = false)
{
bool flag = blockedByShield || this.def.projectile.explosionDelay == 0;
if (flag)
{
this.Explode();
}
else
{
this.landed = true;
this.ticksToDetonation = this.def.projectile.explosionDelay;
GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(this, this.def.projectile.damageDef, this.launcher.Faction, this.launcher);
}
}
protected virtual void Explode()
{
Map map = base.Map;
ModExtension_Cone modExtension = this.def.GetModExtension<ModExtension_Cone>();
this.DoExplosion();
bool flag = modExtension != null;
if (flag)
{
ProjectileProperties projectile = this.def.projectile;
ModExtension_Cone modExtension_Cone = modExtension;
IntVec3 position = base.Position;
Map map2 = map;
Quaternion exactRotation = this.ExactRotation;
DamageDef damageDef = projectile.damageDef;
Thing launcher = base.Launcher;
int damageAmount = this.DamageAmount;
float armorPenetration = this.ArmorPenetration;
SoundDef soundExplode = this.def.projectile.soundExplode;
ThingDef equipmentDef = this.equipmentDef;
ThingDef def = this.def;
Thing thing = this.intendedTarget.Thing;
ThingDef postExplosionSpawnThingDef = null;
float postExplosionSpawnChance = 0f;
int postExplosionSpawnThingCount = 1;
float screenShakeFactor = this.def.projectile.screenShakeFactor;
modExtension_Cone.DoConeExplosion(position, map2, exactRotation, damageDef, launcher, damageAmount, armorPenetration, soundExplode, equipmentDef, def, thing, postExplosionSpawnThingDef, postExplosionSpawnChance, postExplosionSpawnThingCount, null, null, 255, false, null, 0f, 1, 0f, false, null, null, 1f, 0f, null, screenShakeFactor, null, null);
}
bool flag2 = this.def.projectile.explosionEffect != null;
if (flag2)
{
Effecter effecter = this.def.projectile.explosionEffect.Spawn();
bool flag3 = this.def.projectile.explosionEffectLifetimeTicks != 0;
if (flag3)
{
map.effecterMaintainer.AddEffecterToMaintain(effecter, base.Position.ToVector3().ToIntVec3(), this.def.projectile.explosionEffectLifetimeTicks);
}
else
{
effecter.Trigger(new TargetInfo(base.Position, map, false), new TargetInfo(base.Position, map, false), -1);
effecter.Cleanup();
}
}
this.Destroy(DestroyMode.Vanish);
}
protected void DoExplosion()
{
IntVec3 position = base.Position;
float explosionRadius = this.def.projectile.explosionRadius;
DamageDef damageDef = this.def.projectile.damageDef;
Thing launcher = this.launcher;
int damageAmount = this.DamageAmount;
float armorPenetration = this.ArmorPenetration;
SoundDef soundExplode = this.def.projectile.soundExplode;
ThingDef equipmentDef = this.equipmentDef;
ThingDef def = this.def;
Thing thing = this.intendedTarget.Thing;
ThingDef thingDef = this.def.projectile.postExplosionSpawnThingDef ?? this.def.projectile.filth;
ThingDef postExplosionSpawnThingDefWater = this.def.projectile.postExplosionSpawnThingDefWater;
float postExplosionSpawnChance = this.def.projectile.postExplosionSpawnChance;
int postExplosionSpawnThingCount = this.def.projectile.postExplosionSpawnThingCount;
GasType? postExplosionGasType = this.def.projectile.postExplosionGasType;
ThingDef preExplosionSpawnThingDef = this.def.projectile.preExplosionSpawnThingDef;
float preExplosionSpawnChance = this.def.projectile.preExplosionSpawnChance;
int preExplosionSpawnThingCount = this.def.projectile.preExplosionSpawnThingCount;
bool applyDamageToExplosionCellsNeighbors = this.def.projectile.applyDamageToExplosionCellsNeighbors;
ThingDef preExplosionSpawnThingDef2 = preExplosionSpawnThingDef;
float preExplosionSpawnChance2 = preExplosionSpawnChance;
int preExplosionSpawnThingCount2 = preExplosionSpawnThingCount;
float explosionChanceToStartFire = this.def.projectile.explosionChanceToStartFire;
bool explosionDamageFalloff = this.def.projectile.explosionDamageFalloff;
float? direction = new float?(this.origin.AngleToFlat(this.destination));
FloatRange? affectedAngle = null;
float expolosionPropagationSpeed = this.def.projectile.damageDef.expolosionPropagationSpeed;
float screenShakeFactor = this.def.projectile.screenShakeFactor;
IntVec3 center = position;
Map map = base.Map;
float radius = explosionRadius;
DamageDef damType = damageDef;
Thing instigator = launcher;
int damAmount = damageAmount;
float armorPenetration2 = armorPenetration;
SoundDef explosionSound = soundExplode;
ThingDef weapon = equipmentDef;
ThingDef projectile = def;
Thing intendedTarget = thing;
ThingDef postExplosionSpawnThingDef = thingDef;
float postExplosionSpawnChance2 = postExplosionSpawnChance;
int postExplosionSpawnThingCount2 = postExplosionSpawnThingCount;
GasType? postExplosionGasType2 = postExplosionGasType;
bool doExplosionVFX = this.def.projectile.doExplosionVFX;
ThingDef postExplosionSpawnThingDefWater2 = postExplosionSpawnThingDefWater;
GenExplosion.DoExplosion(center, map, radius, damType, instigator, damAmount, armorPenetration2, explosionSound, weapon, projectile, intendedTarget, postExplosionSpawnThingDef, postExplosionSpawnChance2, postExplosionSpawnThingCount2, postExplosionGasType2, null, 255, applyDamageToExplosionCellsNeighbors, preExplosionSpawnThingDef2, preExplosionSpawnChance2, preExplosionSpawnThingCount2, explosionChanceToStartFire, explosionDamageFalloff, direction, null, affectedAngle, doExplosionVFX, expolosionPropagationSpeed, 0f, true, postExplosionSpawnThingDefWater2, screenShakeFactor, null, null, null, null);
}
private int ticksToDetonation;
}
}

View File

@@ -14,6 +14,7 @@ namespace WulaFallenEmpire
public float damageFalloff = 0.25f;
// If true, this projectile will never cause friendly fire, regardless of game settings.
public bool preventFriendlyFire = false;
public FleckDef tailFleckDef; // 用于配置拖尾特效的 FleckDef
}
public class Projectile_WulaLineAttack : Projectile
@@ -21,6 +22,13 @@ namespace WulaFallenEmpire
private int hitCounter = 0;
private List<Thing> alreadyDamaged = new List<Thing>();
private Vector3 lastTickPosition;
private int Fleck_MakeFleckTick; // 拖尾特效的计时器
public int Fleck_MakeFleckTickMax = 1; // 拖尾特效的生成频率
public IntRange Fleck_MakeFleckNum = new IntRange(1, 1); // 每次生成的粒子数量
public FloatRange Fleck_Angle = new FloatRange(-180f, 180f); // 粒子角度
public FloatRange Fleck_Scale = new FloatRange(1f, 1f); // 粒子大小
public FloatRange Fleck_Speed = new FloatRange(0f, 0f); // 粒子速度
public FloatRange Fleck_Rotation = new FloatRange(-180f, 180f); // 粒子旋转
private Wula_PathPierce_Extension Props => def.GetModExtension<Wula_PathPierce_Extension>();
@@ -49,7 +57,38 @@ namespace WulaFallenEmpire
protected override void Tick()
{
Vector3 startPos = this.lastTickPosition;
base.Tick();
base.Tick();
if (this.Destroyed) return;
this.Fleck_MakeFleckTick++;
bool flag = this.Fleck_MakeFleckTick >= this.Fleck_MakeFleckTickMax;
if (flag)
{
this.Fleck_MakeFleckTick = 0;
Map map = base.Map;
int randomInRange = this.Fleck_MakeFleckNum.RandomInRange;
Vector3 vector = this.ExactPosition; // Current position of the bullet
Vector3 vector2 = this.lastTickPosition; // Previous position of the bullet
for (int i = 0; i < randomInRange; i++)
{
float num = (vector - vector2).AngleFlat(); // Angle based on movement direction
float velocityAngle = this.Fleck_Angle.RandomInRange + num;
float randomInRange2 = this.Fleck_Scale.RandomInRange;
float randomInRange3 = this.Fleck_Speed.RandomInRange;
if (Props?.tailFleckDef != null)
{
FleckCreationData dataStatic = FleckMaker.GetDataStatic(vector, map, Props.tailFleckDef, randomInRange2);
dataStatic.rotation = (vector - vector2).AngleFlat();
dataStatic.rotationRate = this.Fleck_Rotation.RandomInRange;
dataStatic.velocityAngle = velocityAngle;
dataStatic.velocitySpeed = randomInRange3;
map.flecks.CreateFleck(dataStatic);
}
}
}
if (this.Destroyed) return;

View File

@@ -1,6 +1,8 @@
using RimWorld;
using System.Collections.Generic;
using Verse.Sound;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Verse;
@@ -20,58 +22,150 @@ namespace WulaFallenEmpire
public float subExplosionSpread = 6f;
public DamageDef subDamageDef;
public SoundDef subSoundExplode;
public FleckDef tailFleckDef; // 用于配置拖尾特效的 FleckDef
public float homingSpeed = 0.1f;
public float initRotateAngle = 30f;
public IntRange destroyTicksAfterLosingTrack = new IntRange(60, 120);
public float speedChangePerTick;
public FloatRange? speedRangeOverride;
public float proximityFuseRange = 0f;
}
public class Projectile_CruiseMissile : Projectile_Explosive
{
private CruiseMissileProperties settings;
private bool flag2;
private Vector3 Randdd;
private Vector3 position2;
public Vector3 ExPos;
protected Vector3 exactPositionInt;
public Vector3 curSpeed;
public bool homing = true;
private Sustainer ambientSustainer;
private List<ThingComp> comps;
private int ticksToDestroy = -1;
// Launch 方法的参数作为字段
// 拖尾特效相关字段
private int Fleck_MakeFleckTick;
public int Fleck_MakeFleckTickMax = 1;
public IntRange Fleck_MakeFleckNum = new IntRange(1, 1);
public FloatRange Fleck_Angle = new FloatRange(-180f, 180f);
public FloatRange Fleck_Scale = new FloatRange(1f, 1f);
public FloatRange Fleck_Speed = new FloatRange(0f, 0f);
public FloatRange Fleck_Rotation = new FloatRange(-180f, 180f);
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
base.SpawnSetup(map, respawningAfterLoad);
settings = def.GetModExtension<CruiseMissileProperties>() ?? new CruiseMissileProperties();
this.ReflectInit();
}
private void RandFactor()
public override void Launch(Thing launcherParam, Vector3 originParam, LocalTargetInfo usedTargetParam, LocalTargetInfo intendedTargetParam, ProjectileHitFlags hitFlagsParam, bool preventFriendlyFireParam = false, Thing equipmentParam = null, ThingDef targetCoverDefParam = null)
{
// 减少垂直方向随机性,调整水平随机范围
Randdd = new Vector3(
Rand.Range(-3f, 3f), // 减小水平随机范围
Rand.Range(8f, 12f), // 降低基础高度
Rand.Range(-3f, 3f)
);
flag2 = true;
this.launcher = launcherParam;
this.origin = originParam;
this.usedTarget = usedTargetParam;
this.intendedTarget = intendedTargetParam;
this.HitFlags = hitFlagsParam;
this.preventFriendlyFire = preventFriendlyFireParam;
this.equipment = equipmentParam;
this.targetCoverDef = targetCoverDefParam;
this.exactPositionInt = origin.Yto0() + Vector3.up * this.def.Altitude;
Vector3 normalized = (this.destination - origin).Yto0().normalized;
float degrees = Rand.Range(-this.settings.initRotateAngle, this.settings.initRotateAngle);
Vector2 vector = new Vector2(normalized.x, normalized.z);
vector = vector.RotatedBy(degrees);
Vector3 a = new Vector3(vector.x, 0f, vector.y);
bool flag6 = this.settings.speedRangeOverride == null;
if (flag6)
{
this.curSpeed = a * this.def.projectile.SpeedTilesPerTick;
}
else
{
this.curSpeed = a * this.settings.speedRangeOverride.Value.RandomInRange;
}
this.ticksToImpact = int.MaxValue;
this.lifetime = int.MaxValue;
}
public Vector3 BPos(float t)
protected void ReflectInit()
{
if (!flag2) RandFactor();
if (NonPublicFields.Projectile_AmbientSustainer == null)
{
NonPublicFields.Projectile_AmbientSustainer = typeof(Projectile).GetField("ambientSustainer", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (NonPublicFields.ThingWithComps_comps == null)
{
NonPublicFields.ThingWithComps_comps = typeof(ThingWithComps).GetField("comps", BindingFlags.Instance | BindingFlags.NonPublic);
}
if (NonPublicFields.ProjectileCheckForFreeInterceptBetween == null)
{
NonPublicFields.ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic);
}
// 计算水平距离
float horizontalDistance = Vector3.Distance(new Vector3(origin.x, 0, origin.z),
new Vector3(destination.x, 0, destination.z));
// 动态调整控制点高度
float arcHeight = Mathf.Clamp(horizontalDistance * 0.2f, 8f, 15f);
Vector3 a = origin + Vector3.forward * horizontalDistance * 0.2f + new Vector3(0f, arcHeight, 0f);
Vector3 a2 = destination - Vector3.forward * horizontalDistance * 0.2f + new Vector3(0f, arcHeight, 0f);
return BezierCurve(origin, a, a2, destination, t);
bool flag = !this.def.projectile.soundAmbient.NullOrUndefined();
if (flag)
{
this.ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this);
}
this.comps = (List<ThingComp>)NonPublicFields.ThingWithComps_comps.GetValue(this);
}
private Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
public float GetHitChance(Thing thing)
{
float u = 1 - t;
return u * u * u * p0
+ 3 * u * u * t * p1
+ 3 * u * t * t * p2
+ t * t * t * p3;
float num = this.settings.homingSpeed;
bool flag = thing == null;
float result;
if (flag)
{
result = num;
}
else
{
Pawn pawn = thing as Pawn;
bool flag2 = pawn != null;
if (flag2)
{
num *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f);
bool flag3 = pawn.GetPosture() > PawnPosture.Standing;
if (flag3)
{
num *= 0.5f;
}
float num2 = 1f;
switch (this.equipmentQuality)
{
case QualityCategory.Awful:
num2 = 0.5f;
goto IL_DD;
case QualityCategory.Poor:
num2 = 0.75f;
goto IL_DD;
case QualityCategory.Normal:
num2 = 1f;
goto IL_DD;
case QualityCategory.Excellent:
num2 = 1.1f;
goto IL_DD;
case QualityCategory.Masterwork:
num2 = 1.2f;
goto IL_DD;
case QualityCategory.Legendary:
num2 = 1.3f;
goto IL_DD;
}
Log.Message("Unknown QualityCategory, returning default qualityFactor = 1");
IL_DD:
num *= num2;
}
else
{
num *= 1.5f * thing.def.fillPercent;
}
result = Mathf.Clamp(num, 0f, 1f);
}
return result;
}
private IEnumerable<IntVec3> GetValidCells(Map map)
@@ -136,43 +230,149 @@ namespace WulaFallenEmpire
);
}
protected override void DrawAt(Vector3 position, bool flip = false)
public override Quaternion ExactRotation
{
position2 = BPos(DistanceCoveredFraction - 0.01f);
ExPos = position = BPos(DistanceCoveredFraction);
base.DrawAt(position, flip);
get
{
return Quaternion.LookRotation(this.curSpeed);
}
}
public override Vector3 ExactPosition
{
get
{
return this.exactPositionInt;
}
}
protected override void Tick()
{
if (intendedTarget.Thing is Pawn pawn && pawn.Spawned && !pawn.Destroyed)
this.ThingWithCompsTick();
this.lifetime--;
if (this.settings.tailFleckDef != null)
{
if ((pawn.Dead || pawn.Downed) && DistanceCoveredFraction < 0.6f)
this.Fleck_MakeFleckTick++;
if (this.Fleck_MakeFleckTick >= this.Fleck_MakeFleckTickMax)
{
FindNextTarget(pawn.DrawPos);
this.Fleck_MakeFleckTick = 0;
for (int i = 0; i < this.Fleck_MakeFleckNum.RandomInRange; i++)
{
FleckMaker.Static(this.ExactPosition + Gen.RandomHorizontalVector(this.Fleck_Scale.RandomInRange / 2f), base.Map, this.settings.tailFleckDef, this.Fleck_Scale.RandomInRange);
}
}
}
bool landed = this.landed;
if (!landed)
{
Vector3 exactPosition = this.ExactPosition;
this.ticksToImpact--;
this.MovementTick();
bool flag = !this.ExactPosition.InBounds(base.Map);
if (flag)
{
base.Position = exactPosition.ToIntVec3();
this.Destroy(DestroyMode.Vanish);
}
else
{
Vector3 exactPosition2 = this.ExactPosition;
object[] parameters = new object[]
{
exactPosition,
exactPosition2
};
bool flag2 = (bool)NonPublicFields.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters);
if (!flag2)
{
base.Position = this.ExactPosition.ToIntVec3();
bool flag3 = this.ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null;
if (flag3)
{
this.def.projectile.soundImpactAnticipate.PlayOneShot(this);
}
bool flag4 = this.ticksToImpact <= 0;
if (flag4)
{
this.Impact(null);
}
else
{
bool flag5 = this.ambientSustainer != null;
if (flag5)
{
this.ambientSustainer.Maintain();
}
}
}
}
destination = pawn.DrawPos;
}
base.Tick();
}
private void FindNextTarget(Vector3 center)
private void MovementTick()
{
var map = base.Map;
if (map == null) return;
foreach (IntVec3 cell in GenRadial.RadialCellsAround(IntVec3.FromVector3(center), 7f, true))
if (this.homing)
{
if (!cell.InBounds(map)) continue;
Pawn target = cell.GetFirstPawn(map);
if (target != null && target.Faction.HostileTo(launcher?.Faction))
if (this.intendedTarget != null && this.intendedTarget.Thing != null)
{
intendedTarget = target;
Vector3 vector = (this.intendedTarget.Thing.DrawPos - this.exactPositionInt).normalized;
this.curSpeed = Vector3.RotateTowards(this.curSpeed, vector * this.curSpeed.magnitude, this.settings.homingSpeed, 0f);
}
else if (this.ticksToDestroy == -1)
{
this.ticksToDestroy = this.settings.destroyTicksAfterLosingTrack.RandomInRange;
}
}
if (this.ticksToDestroy > 0)
{
this.ticksToDestroy--;
if (this.ticksToDestroy == 0)
{
this.Destroy(DestroyMode.Vanish);
return;
}
}
intendedTarget = CellRect.CenteredOn(IntVec3.FromVector3(center), 7).RandomCell;
if (this.settings.speedChangePerTick != 0f)
{
this.curSpeed = this.curSpeed.normalized * (this.curSpeed.magnitude + this.settings.speedChangePerTick);
}
if (this.settings.proximityFuseRange > 0f)
{
if (this.intendedTarget != null && this.intendedTarget.Thing != null && (this.intendedTarget.Thing.DrawPos - this.exactPositionInt).magnitude < this.settings.proximityFuseRange)
{
this.Impact(null);
return;
}
}
this.exactPositionInt += this.curSpeed;
}
protected void ThingWithCompsTick()
{
if (this.comps != null)
{
for (int i = 0; i < this.comps.Count; i++)
{
this.comps[i].CompTick();
}
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look<Vector3>(ref this.exactPositionInt, "exactPosition", default(Vector3), false);
Scribe_Values.Look<Vector3>(ref this.curSpeed, "curSpeed", default(Vector3), false);
Scribe_Values.Look<bool>(ref this.homing, "homing", true, false);
Scribe_Values.Look<int>(ref this.ticksToDestroy, "ticksToDestroy", -1, false);
}
}
public static class NonPublicFields
{
public static FieldInfo Projectile_AmbientSustainer;
public static FieldInfo ThingWithComps_comps;
public static MethodInfo ProjectileCheckForFreeInterceptBetween;
}
}

View File

@@ -124,6 +124,10 @@
<Compile Include="NeedDefExtension_Energy.cs" />
<Compile Include="Projectile_WulaPenetrating.cs" />
<Compile Include="Projectile_WulaBeam.cs" />
<Compile Include="Projectile_Homing.cs" />
<Compile Include="Projectile_Homing_Explosive.cs" />
<Compile Include="ModExtension_Cone.cs" />
<Compile Include="HomingProjectileDef.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PsychicRitual_TechOffering.cs" />
<Compile Include="PsychicRitualDef_AddHediff.cs" />