暂存
This commit is contained in:
Binary file not shown.
98
Source/WulaFallenEmpire/BulletWithTrail.cs
Normal file
98
Source/WulaFallenEmpire/BulletWithTrail.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
using Verse.Sound;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class BulletWithTrail : Bullet
|
||||||
|
{
|
||||||
|
private TrackingBulletDef trackingDefInt;
|
||||||
|
private int Fleck_MakeFleckTick;
|
||||||
|
private Vector3 lastTickPosition;
|
||||||
|
|
||||||
|
public TrackingBulletDef TrackingDef
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (trackingDefInt == null)
|
||||||
|
{
|
||||||
|
trackingDefInt = def.GetModExtension<TrackingBulletDef>();
|
||||||
|
if (trackingDefInt == null)
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"TrackingBulletDef for {this.def.defName} is null. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
||||||
|
this.trackingDefInt = new TrackingBulletDef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trackingDefInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null)
|
||||||
|
{
|
||||||
|
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
|
||||||
|
lastTickPosition = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Tick()
|
||||||
|
{
|
||||||
|
base.Tick();
|
||||||
|
|
||||||
|
// 处理拖尾特效
|
||||||
|
if (TrackingDef != null && TrackingDef.tailFleckDef != null)
|
||||||
|
{
|
||||||
|
Fleck_MakeFleckTick++;
|
||||||
|
if (Fleck_MakeFleckTick >= TrackingDef.fleckDelayTicks)
|
||||||
|
{
|
||||||
|
if (Fleck_MakeFleckTick >= (TrackingDef.fleckDelayTicks + TrackingDef.fleckMakeFleckTickMax))
|
||||||
|
{
|
||||||
|
Fleck_MakeFleckTick = TrackingDef.fleckDelayTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map map = base.Map;
|
||||||
|
int randomInRange = TrackingDef.fleckMakeFleckNum.RandomInRange;
|
||||||
|
Vector3 currentPosition = base.ExactPosition;
|
||||||
|
Vector3 previousPosition = lastTickPosition;
|
||||||
|
|
||||||
|
for (int i = 0; i < randomInRange; i++)
|
||||||
|
{
|
||||||
|
float num = (currentPosition - previousPosition).AngleFlat();
|
||||||
|
float velocityAngle = TrackingDef.fleckAngle.RandomInRange + num;
|
||||||
|
float randomInRange2 = TrackingDef.fleckScale.RandomInRange;
|
||||||
|
float randomInRange3 = TrackingDef.fleckSpeed.RandomInRange;
|
||||||
|
|
||||||
|
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, randomInRange2);
|
||||||
|
dataStatic.rotation = (currentPosition - previousPosition).AngleFlat();
|
||||||
|
dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange;
|
||||||
|
dataStatic.velocityAngle = velocityAngle;
|
||||||
|
dataStatic.velocitySpeed = randomInRange3;
|
||||||
|
map.flecks.CreateFleck(dataStatic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTickPosition = base.ExactPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData()
|
||||||
|
{
|
||||||
|
base.ExposeData();
|
||||||
|
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
|
||||||
|
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
|
||||||
|
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||||
|
{
|
||||||
|
if (this.trackingDefInt == null)
|
||||||
|
{
|
||||||
|
this.trackingDefInt = this.def.GetModExtension<TrackingBulletDef>();
|
||||||
|
if (this.trackingDefInt == null)
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"TrackingBulletDef is null for projectile {this.def.defName} after PostLoadInit. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
||||||
|
this.trackingDefInt = new TrackingBulletDef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Verse;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class HomingProjectileDef : DefModExtension
|
|
||||||
{
|
|
||||||
public float SpeedChangeTilesPerTickOverride;
|
|
||||||
public FloatRange SpeedRangeTilesPerTickOverride;
|
|
||||||
|
|
||||||
public float hitChance = 0.5f;
|
|
||||||
|
|
||||||
public float homingSpeed = 0.1f;
|
|
||||||
|
|
||||||
public float initRotateAngle = 30f;
|
|
||||||
|
|
||||||
public float proximityFuseRange = 0.5f; // 调整默认值,使其在接近目标时能正确触发引信
|
|
||||||
|
|
||||||
public IntRange destroyTicksAfterLosingTrack = new IntRange(60, 120);
|
|
||||||
|
|
||||||
public ThingDef extraProjectile;
|
|
||||||
|
|
||||||
public float speedChangePerTick;
|
|
||||||
|
|
||||||
public FloatRange? speedRangeOverride;
|
|
||||||
public FleckDef tailFleckDef;
|
|
||||||
// 拖尾特效的详细配置参数
|
|
||||||
public int fleckMakeFleckTickMax = 1;
|
|
||||||
public IntRange fleckMakeFleckNum = new IntRange(1, 1);
|
|
||||||
public FloatRange fleckAngle = new FloatRange(-180f, 180f);
|
|
||||||
public FloatRange fleckScale = new FloatRange(1f, 1f);
|
|
||||||
public FloatRange fleckSpeed = new FloatRange(0f, 0f);
|
|
||||||
public FloatRange fleckRotation = new FloatRange(-180f, 180f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -60,6 +60,9 @@ namespace WulaFallenEmpire
|
|||||||
this.landed = true;
|
this.landed = true;
|
||||||
this.ticksToDetonation = ExplosiveDef.explosionDelay; // Use ExplosiveDef for explosionDelay
|
this.ticksToDetonation = ExplosiveDef.explosionDelay; // Use ExplosiveDef for explosionDelay
|
||||||
GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(this, ExplosiveDef.damageDef ?? DamageDefOf.Bomb, this.launcher.Faction, this.launcher); // Use ExplosiveDef for damageDef
|
GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(this, ExplosiveDef.damageDef ?? DamageDefOf.Bomb, this.launcher.Faction, this.launcher); // Use ExplosiveDef for damageDef
|
||||||
|
// 停止追踪并清空速度,确保子弹停止移动
|
||||||
|
this.homing = false;
|
||||||
|
this.curSpeed = Vector3.zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ namespace WulaFallenEmpire
|
|||||||
bool doExplosionVFX = ExplosiveDef.doExplosionVFX; // Use ExplosiveDef for doExplosionVFX
|
bool doExplosionVFX = ExplosiveDef.doExplosionVFX; // Use ExplosiveDef for doExplosionVFX
|
||||||
|
|
||||||
GenExplosion.DoExplosion(
|
GenExplosion.DoExplosion(
|
||||||
center: position, // 爆炸中心
|
center: ExactPosition.ToIntVec3(), // 爆炸中心
|
||||||
map: map, // 地图
|
map: map, // 地图
|
||||||
radius: explosionRadius, // 爆炸半径
|
radius: explosionRadius, // 爆炸半径
|
||||||
damType: damageDef, // 伤害类型
|
damType: damageDef, // 伤害类型
|
||||||
|
|||||||
98
Source/WulaFallenEmpire/Projectile_ExplosiveWithTrail.cs
Normal file
98
Source/WulaFallenEmpire/Projectile_ExplosiveWithTrail.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
using Verse.Sound;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class Projectile_ExplosiveWithTrail : Projectile_Explosive
|
||||||
|
{
|
||||||
|
private TrackingBulletDef trackingDefInt;
|
||||||
|
private int Fleck_MakeFleckTick;
|
||||||
|
private Vector3 lastTickPosition;
|
||||||
|
|
||||||
|
public TrackingBulletDef TrackingDef
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (trackingDefInt == null)
|
||||||
|
{
|
||||||
|
trackingDefInt = def.GetModExtension<TrackingBulletDef>();
|
||||||
|
if (trackingDefInt == null)
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"TrackingBulletDef for {this.def.defName} is null. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
||||||
|
this.trackingDefInt = new TrackingBulletDef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trackingDefInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null)
|
||||||
|
{
|
||||||
|
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
|
||||||
|
lastTickPosition = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Tick()
|
||||||
|
{
|
||||||
|
base.Tick();
|
||||||
|
|
||||||
|
// 处理拖尾特效
|
||||||
|
if (TrackingDef != null && TrackingDef.tailFleckDef != null)
|
||||||
|
{
|
||||||
|
Fleck_MakeFleckTick++;
|
||||||
|
if (Fleck_MakeFleckTick >= TrackingDef.fleckDelayTicks)
|
||||||
|
{
|
||||||
|
if (Fleck_MakeFleckTick >= (TrackingDef.fleckDelayTicks + TrackingDef.fleckMakeFleckTickMax))
|
||||||
|
{
|
||||||
|
Fleck_MakeFleckTick = TrackingDef.fleckDelayTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map map = base.Map;
|
||||||
|
int randomInRange = TrackingDef.fleckMakeFleckNum.RandomInRange;
|
||||||
|
Vector3 currentPosition = base.ExactPosition;
|
||||||
|
Vector3 previousPosition = lastTickPosition;
|
||||||
|
|
||||||
|
for (int i = 0; i < randomInRange; i++)
|
||||||
|
{
|
||||||
|
float num = (currentPosition - previousPosition).AngleFlat();
|
||||||
|
float velocityAngle = TrackingDef.fleckAngle.RandomInRange + num;
|
||||||
|
float randomInRange2 = TrackingDef.fleckScale.RandomInRange;
|
||||||
|
float randomInRange3 = TrackingDef.fleckSpeed.RandomInRange;
|
||||||
|
|
||||||
|
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, randomInRange2);
|
||||||
|
dataStatic.rotation = (currentPosition - previousPosition).AngleFlat();
|
||||||
|
dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange;
|
||||||
|
dataStatic.velocityAngle = velocityAngle;
|
||||||
|
dataStatic.velocitySpeed = randomInRange3;
|
||||||
|
map.flecks.CreateFleck(dataStatic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTickPosition = base.ExactPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData()
|
||||||
|
{
|
||||||
|
base.ExposeData();
|
||||||
|
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
|
||||||
|
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
|
||||||
|
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||||
|
{
|
||||||
|
if (this.trackingDefInt == null)
|
||||||
|
{
|
||||||
|
this.trackingDefInt = this.def.GetModExtension<TrackingBulletDef>();
|
||||||
|
if (this.trackingDefInt == null)
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"TrackingBulletDef is null for projectile {this.def.defName} after PostLoadInit. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
||||||
|
this.trackingDefInt = new TrackingBulletDef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
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
|
|
||||||
{
|
|
||||||
private HomingProjectileDef homingDefInt;
|
|
||||||
|
|
||||||
private Sustainer ambientSustainer;
|
|
||||||
|
|
||||||
private List<ThingComp> comps;
|
|
||||||
|
|
||||||
protected Vector3 exactPositionInt;
|
|
||||||
|
|
||||||
public Vector3 curSpeed;
|
|
||||||
|
|
||||||
public bool homing = true;
|
|
||||||
private int Fleck_MakeFleckTick; // 拖尾特效的计时器
|
|
||||||
private Vector3 lastTickPosition; // 记录上一帧的位置,用于计算移动方向
|
|
||||||
|
|
||||||
private static class NonPublicFields
|
|
||||||
{
|
|
||||||
public static FieldInfo Projectile_AmbientSustainer = typeof(Projectile).GetField("ambientSustainer", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
public static FieldInfo ThingWithComps_comps = typeof(ThingWithComps).GetField("comps", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
public static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HomingProjectileDef HomingDef
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (homingDefInt == null)
|
|
||||||
{
|
|
||||||
homingDefInt = def.GetModExtension<HomingProjectileDef>();
|
|
||||||
if (homingDefInt == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce($"HomingProjectileDef for {this.def.defName} is null. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
this.homingDefInt = new HomingProjectileDef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return homingDefInt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Vector3 ExactPosition => exactPositionInt;
|
|
||||||
|
|
||||||
public override Quaternion ExactRotation => Quaternion.LookRotation(curSpeed);
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (usedTarget.HasThing && usedTarget.Thing is IAttackTarget)
|
|
||||||
{
|
|
||||||
if (Rand.Chance(GetHitChance(usedTarget.Thing)))
|
|
||||||
{
|
|
||||||
hitFlags |= ProjectileHitFlags.IntendedTarget;
|
|
||||||
intendedTarget = usedTarget;
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Rand.Chance(GetHitChance(intendedTarget.Thing)))
|
|
||||||
{
|
|
||||||
hitFlags |= ProjectileHitFlags.IntendedTarget;
|
|
||||||
usedTarget = intendedTarget;
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
hitFlags &= ~ProjectileHitFlags.IntendedTarget;
|
|
||||||
}
|
|
||||||
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
|
|
||||||
exactPositionInt = origin.Yto0() + Vector3.up * def.Altitude;
|
|
||||||
lastTickPosition = origin; // 初始化 lastTickPosition
|
|
||||||
Vector3 normalized = (destination - origin).Yto0().normalized;
|
|
||||||
float degrees = Rand.Range(0f - HomingDef.initRotateAngle, HomingDef.initRotateAngle);
|
|
||||||
Vector2 v = new Vector2(normalized.x, normalized.z);
|
|
||||||
v = v.RotatedBy(degrees);
|
|
||||||
Vector3 vector = new Vector3(v.x, 0f, v.y);
|
|
||||||
// 检查 HomingDef.speedRangeOverride 是否有值
|
|
||||||
if (!HomingDef.speedRangeOverride.HasValue)
|
|
||||||
{
|
|
||||||
curSpeed = vector * def.projectile.SpeedTilesPerTick;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curSpeed = vector * HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange;
|
|
||||||
}
|
|
||||||
ReflectInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ReflectInit()
|
|
||||||
{
|
|
||||||
if (!def.projectile.soundAmbient.NullOrUndefined())
|
|
||||||
{
|
|
||||||
ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this);
|
|
||||||
}
|
|
||||||
comps = (List<ThingComp>)NonPublicFields.ThingWithComps_comps.GetValue(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetHitChance(Thing thing)
|
|
||||||
{
|
|
||||||
if (this.HomingDef == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce("HomingDef is null for projectile " + this.def.defName + ". Returning default hitChance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
return 0.7f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float hitChance = HomingDef.hitChance;
|
|
||||||
if (thing == null)
|
|
||||||
{
|
|
||||||
return hitChance;
|
|
||||||
}
|
|
||||||
if (thing is Pawn pawn)
|
|
||||||
{
|
|
||||||
hitChance *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f);
|
|
||||||
if (pawn.GetPosture() != 0)
|
|
||||||
{
|
|
||||||
hitChance *= 0.5f;
|
|
||||||
}
|
|
||||||
float num = 1f;
|
|
||||||
switch (equipmentQuality)
|
|
||||||
{
|
|
||||||
case QualityCategory.Awful:
|
|
||||||
num = 0.5f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Poor:
|
|
||||||
num = 0.75f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Normal:
|
|
||||||
num = 1f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Excellent:
|
|
||||||
num = 1.1f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Masterwork:
|
|
||||||
num = 1.2f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Legendary:
|
|
||||||
num = 1.3f;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.Message("Unknown QualityCategory, returning default qualityFactor = 1");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hitChance *= num;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hitChance *= 1.5f * thing.def.fillPercent;
|
|
||||||
}
|
|
||||||
return Mathf.Clamp(hitChance, 0f, 1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void MovementTick()
|
|
||||||
{
|
|
||||||
Vector3 vect = ExactPosition + curSpeed;
|
|
||||||
ShootLine shootLine = new ShootLine(ExactPosition.ToIntVec3(), vect.ToIntVec3());
|
|
||||||
Vector3 vector = (intendedTarget.Cell.ToVector3() - ExactPosition).Yto0();
|
|
||||||
if (homing)
|
|
||||||
{
|
|
||||||
Vector3 vector2 = vector.normalized - curSpeed.normalized;
|
|
||||||
if (vector2.sqrMagnitude >= 1.414f)
|
|
||||||
{
|
|
||||||
homing = false;
|
|
||||||
lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
|
|
||||||
ticksToImpact = lifetime;
|
|
||||||
base.HitFlags &= ~ProjectileHitFlags.IntendedTarget;
|
|
||||||
base.HitFlags |= ProjectileHitFlags.NonTargetPawns;
|
|
||||||
base.HitFlags |= ProjectileHitFlags.NonTargetWorld;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curSpeed += vector2 * HomingDef.homingSpeed * curSpeed.magnitude;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (IntVec3 item in shootLine.Points())
|
|
||||||
{
|
|
||||||
if (!((intendedTarget.Cell - item).SqrMagnitude <= HomingDef.proximityFuseRange * HomingDef.proximityFuseRange))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
homing = false;
|
|
||||||
lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
|
|
||||||
if ((base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || HomingDef.proximityFuseRange > 0f)
|
|
||||||
{
|
|
||||||
lifetime = 0;
|
|
||||||
ticksToImpact = 0;
|
|
||||||
vect = item.ToVector3();
|
|
||||||
if (Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null)
|
|
||||||
{
|
|
||||||
def.projectile.soundImpactAnticipate.PlayOneShot(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exactPositionInt = vect;
|
|
||||||
curSpeed *= (curSpeed.magnitude + HomingDef.SpeedChangeTilesPerTickOverride) / curSpeed.magnitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Tick()
|
|
||||||
{
|
|
||||||
ThingWithCompsTick();
|
|
||||||
lifetime--;
|
|
||||||
|
|
||||||
if (lifetime <= 0)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理拖尾特效
|
|
||||||
if (HomingDef != null && HomingDef.tailFleckDef != null)
|
|
||||||
{
|
|
||||||
Fleck_MakeFleckTick++;
|
|
||||||
if (Fleck_MakeFleckTick >= HomingDef.fleckMakeFleckTickMax)
|
|
||||||
{
|
|
||||||
Fleck_MakeFleckTick = 0;
|
|
||||||
Map map = base.Map;
|
|
||||||
int randomInRange = HomingDef.fleckMakeFleckNum.RandomInRange;
|
|
||||||
Vector3 currentPosition = ExactPosition;
|
|
||||||
Vector3 previousPosition = lastTickPosition;
|
|
||||||
|
|
||||||
for (int i = 0; i < randomInRange; i++)
|
|
||||||
{
|
|
||||||
float num = (currentPosition - previousPosition).AngleFlat();
|
|
||||||
float velocityAngle = HomingDef.fleckAngle.RandomInRange + num;
|
|
||||||
float randomInRange2 = HomingDef.fleckScale.RandomInRange;
|
|
||||||
float randomInRange3 = HomingDef.fleckSpeed.RandomInRange;
|
|
||||||
|
|
||||||
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, HomingDef.tailFleckDef, randomInRange2);
|
|
||||||
dataStatic.rotation = (currentPosition - previousPosition).AngleFlat();
|
|
||||||
dataStatic.rotationRate = HomingDef.fleckRotation.RandomInRange;
|
|
||||||
dataStatic.velocityAngle = velocityAngle;
|
|
||||||
dataStatic.velocitySpeed = randomInRange3;
|
|
||||||
map.flecks.CreateFleck(dataStatic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTickPosition = ExactPosition; // 更新上一帧位置
|
|
||||||
|
|
||||||
// 移除 if (landed) return; 以确保子弹落地后也能正常销毁
|
|
||||||
Vector3 exactPosition = ExactPosition;
|
|
||||||
ticksToImpact--;
|
|
||||||
MovementTick();
|
|
||||||
if (!ExactPosition.InBounds(base.Map))
|
|
||||||
{
|
|
||||||
base.Position = exactPosition.ToIntVec3();
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Vector3 exactPosition2 = ExactPosition;
|
|
||||||
object[] parameters = new object[2] { exactPosition, exactPosition2 };
|
|
||||||
if (!(bool)NonPublicFields.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters))
|
|
||||||
{
|
|
||||||
base.Position = ExactPosition.ToIntVec3();
|
|
||||||
if (ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null)
|
|
||||||
{
|
|
||||||
def.projectile.soundImpactAnticipate.PlayOneShot(this);
|
|
||||||
}
|
|
||||||
if (ticksToImpact <= 0)
|
|
||||||
{
|
|
||||||
ImpactSomething();
|
|
||||||
}
|
|
||||||
else if (ambientSustainer != null)
|
|
||||||
{
|
|
||||||
ambientSustainer.Maintain();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThingWithCompsTick()
|
|
||||||
{
|
|
||||||
if (comps != null)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (int count = comps.Count; i < count; i++)
|
|
||||||
{
|
|
||||||
comps[i].CompTick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Impact(Thing hitThing, bool blockedByShield = false)
|
|
||||||
{
|
|
||||||
Map map = base.Map;
|
|
||||||
IntVec3 position = base.Position;
|
|
||||||
base.Impact(hitThing, blockedByShield);
|
|
||||||
if (HomingDef.extraProjectile != null)
|
|
||||||
{
|
|
||||||
if (hitThing != null && hitThing.Spawned)
|
|
||||||
{
|
|
||||||
((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, position, position, ProjectileHitFlags.All, false, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ExposeData()
|
|
||||||
{
|
|
||||||
base.ExposeData();
|
|
||||||
Scribe_Values.Look(ref exactPositionInt, "exactPosition");
|
|
||||||
Scribe_Values.Look(ref curSpeed, "curSpeed");
|
|
||||||
Scribe_Values.Look(ref homing, "homing", defaultValue: false);
|
|
||||||
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
|
||||||
{
|
|
||||||
ReflectInit();
|
|
||||||
if (this.homingDefInt == null)
|
|
||||||
{
|
|
||||||
this.homingDefInt = this.def.GetModExtension<HomingProjectileDef>();
|
|
||||||
if (this.homingDefInt == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce($"HomingProjectileDef is null for projectile {this.def.defName} after PostLoadInit. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
this.homingDefInt = new HomingProjectileDef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,726 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using RimWorld;
|
|
||||||
using UnityEngine;
|
|
||||||
using Verse;
|
|
||||||
using Verse.Sound;
|
|
||||||
using System.Reflection;
|
|
||||||
using Verse.AI;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class Projectile_PoiBullet : Bullet
|
|
||||||
{
|
|
||||||
// Projectile_Homing 的字段
|
|
||||||
private HomingProjectileDef homingDefInt;
|
|
||||||
private Sustainer ambientSustainer;
|
|
||||||
private List<ThingComp> comps;
|
|
||||||
protected Vector3 exactPositionInt;
|
|
||||||
public Vector3 curSpeed;
|
|
||||||
public bool homing = true;
|
|
||||||
private int Fleck_MakeFleckTick;
|
|
||||||
private Vector3 lastTickPosition;
|
|
||||||
|
|
||||||
// Projectile_Homing_Explosive 的字段
|
|
||||||
private int ticksToDetonation;
|
|
||||||
|
|
||||||
private static class NonPublicFields
|
|
||||||
{
|
|
||||||
public static FieldInfo Projectile_AmbientSustainer = typeof(Projectile).GetField("ambientSustainer", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
public static FieldInfo ThingWithComps_comps = typeof(ThingWithComps).GetField("comps", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
public static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HomingProjectileDef HomingDef
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (homingDefInt == null)
|
|
||||||
{
|
|
||||||
homingDefInt = def.GetModExtension<HomingProjectileDef>();
|
|
||||||
if (homingDefInt == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce($"HomingProjectileDef for {this.def.defName} is null. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
this.homingDefInt = new HomingProjectileDef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return homingDefInt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Vector3 ExactPosition => exactPositionInt;
|
|
||||||
|
|
||||||
public override Quaternion ExactRotation => Quaternion.LookRotation(curSpeed);
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (usedTarget.HasThing && usedTarget.Thing is IAttackTarget)
|
|
||||||
{
|
|
||||||
if (Rand.Chance(GetHitChance(usedTarget.Thing)))
|
|
||||||
{
|
|
||||||
hitFlags |= ProjectileHitFlags.IntendedTarget;
|
|
||||||
intendedTarget = usedTarget;
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Rand.Chance(GetHitChance(intendedTarget.Thing)))
|
|
||||||
{
|
|
||||||
hitFlags |= ProjectileHitFlags.IntendedTarget;
|
|
||||||
usedTarget = intendedTarget;
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
hitFlags &= ~ProjectileHitFlags.IntendedTarget;
|
|
||||||
}
|
|
||||||
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
|
|
||||||
exactPositionInt = origin.Yto0() + Vector3.up * def.Altitude;
|
|
||||||
lastTickPosition = origin;
|
|
||||||
Vector3 normalized = (destination - origin).Yto0().normalized;
|
|
||||||
float degrees = Rand.Range(0f - HomingDef.initRotateAngle, HomingDef.initRotateAngle);
|
|
||||||
Vector2 v = new Vector2(normalized.x, normalized.z);
|
|
||||||
v = v.RotatedBy(degrees);
|
|
||||||
Vector3 vector = new Vector3(v.x, 0f, v.y);
|
|
||||||
if (!HomingDef.speedRangeOverride.HasValue)
|
|
||||||
{
|
|
||||||
curSpeed = vector * def.projectile.SpeedTilesPerTick;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curSpeed = vector * HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange;
|
|
||||||
}
|
|
||||||
ReflectInit();
|
|
||||||
|
|
||||||
// Projectile_PoiBullet 原始逻辑中的部分初始化
|
|
||||||
this.flag2 = false; // 重置RandFactor的标志
|
|
||||||
this.flag3 = true; // 重置CanHitTarget的标志
|
|
||||||
this.CalHit = false; // 重置命中计算结果
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ReflectInit()
|
|
||||||
{
|
|
||||||
if (!def.projectile.soundAmbient.NullOrUndefined())
|
|
||||||
{
|
|
||||||
ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this);
|
|
||||||
}
|
|
||||||
comps = (List<ThingComp>)NonPublicFields.ThingWithComps_comps.GetValue(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetHitChance(Thing thing)
|
|
||||||
{
|
|
||||||
if (this.HomingDef == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce("HomingDef is null for projectile " + this.def.defName + ". Returning default hitChance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
return 0.7f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float hitChance = HomingDef.hitChance;
|
|
||||||
if (thing == null)
|
|
||||||
{
|
|
||||||
return hitChance;
|
|
||||||
}
|
|
||||||
if (thing is Pawn pawn)
|
|
||||||
{
|
|
||||||
hitChance *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f);
|
|
||||||
if (pawn.GetPosture() != 0)
|
|
||||||
{
|
|
||||||
hitChance *= 0.5f;
|
|
||||||
}
|
|
||||||
float num = 1f;
|
|
||||||
switch (equipmentQuality)
|
|
||||||
{
|
|
||||||
case QualityCategory.Awful:
|
|
||||||
num = 0.5f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Poor:
|
|
||||||
num = 0.75f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Normal:
|
|
||||||
num = 1f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Excellent:
|
|
||||||
num = 1.1f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Masterwork:
|
|
||||||
num = 1.2f;
|
|
||||||
break;
|
|
||||||
case QualityCategory.Legendary:
|
|
||||||
num = 1.3f;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.Message("Unknown QualityCategory, returning default qualityFactor = 1");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hitChance *= num;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hitChance *= 1.5f * thing.def.fillPercent;
|
|
||||||
}
|
|
||||||
return Mathf.Clamp(hitChance, 0f, 1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void MovementTick()
|
|
||||||
{
|
|
||||||
Vector3 vect = ExactPosition + curSpeed;
|
|
||||||
ShootLine shootLine = new ShootLine(ExactPosition.ToIntVec3(), vect.ToIntVec3());
|
|
||||||
Vector3 vector = (intendedTarget.Cell.ToVector3() - ExactPosition).Yto0();
|
|
||||||
if (homing)
|
|
||||||
{
|
|
||||||
Vector3 vector2 = vector.normalized - curSpeed.normalized;
|
|
||||||
if (vector2.sqrMagnitude >= 1.414f)
|
|
||||||
{
|
|
||||||
homing = false;
|
|
||||||
lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
|
|
||||||
ticksToImpact = lifetime;
|
|
||||||
base.HitFlags &= ~ProjectileHitFlags.IntendedTarget;
|
|
||||||
base.HitFlags |= ProjectileHitFlags.NonTargetPawns;
|
|
||||||
base.HitFlags |= ProjectileHitFlags.NonTargetWorld;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curSpeed += vector2 * HomingDef.homingSpeed * curSpeed.magnitude;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (IntVec3 item in shootLine.Points())
|
|
||||||
{
|
|
||||||
if (!((intendedTarget.Cell - item).SqrMagnitude <= HomingDef.proximityFuseRange * HomingDef.proximityFuseRange))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
homing = false;
|
|
||||||
lifetime = HomingDef.destroyTicksAfterLosingTrack.RandomInRange;
|
|
||||||
if ((base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || HomingDef.proximityFuseRange > 0f)
|
|
||||||
{
|
|
||||||
lifetime = 0;
|
|
||||||
ticksToImpact = 0;
|
|
||||||
vect = item.ToVector3();
|
|
||||||
if (Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null)
|
|
||||||
{
|
|
||||||
def.projectile.soundImpactAnticipate.PlayOneShot(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exactPositionInt = vect;
|
|
||||||
curSpeed *= (curSpeed.magnitude + HomingDef.SpeedChangeTilesPerTickOverride) / curSpeed.magnitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Tick()
|
|
||||||
{
|
|
||||||
// Projectile_Homing 的 Tick 逻辑
|
|
||||||
ThingWithCompsTick();
|
|
||||||
lifetime--;
|
|
||||||
|
|
||||||
if (lifetime <= 0)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理拖尾特效
|
|
||||||
if (HomingDef != null && HomingDef.tailFleckDef != null)
|
|
||||||
{
|
|
||||||
Fleck_MakeFleckTick++;
|
|
||||||
if (Fleck_MakeFleckTick >= HomingDef.fleckMakeFleckTickMax)
|
|
||||||
{
|
|
||||||
Fleck_MakeFleckTick = 0;
|
|
||||||
Map map = base.Map;
|
|
||||||
int randomInRange = HomingDef.fleckMakeFleckNum.RandomInRange;
|
|
||||||
Vector3 currentPosition = ExactPosition;
|
|
||||||
Vector3 previousPosition = lastTickPosition;
|
|
||||||
|
|
||||||
for (int i = 0; i < randomInRange; i++)
|
|
||||||
{
|
|
||||||
float num = (currentPosition - previousPosition).AngleFlat();
|
|
||||||
float velocityAngle = HomingDef.fleckAngle.RandomInRange + num;
|
|
||||||
float randomInRange2 = HomingDef.fleckScale.RandomInRange;
|
|
||||||
float randomInRange3 = HomingDef.fleckSpeed.RandomInRange;
|
|
||||||
|
|
||||||
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, HomingDef.tailFleckDef, randomInRange2);
|
|
||||||
dataStatic.rotation = (currentPosition - previousPosition).AngleFlat();
|
|
||||||
dataStatic.rotationRate = HomingDef.fleckRotation.RandomInRange;
|
|
||||||
dataStatic.velocityAngle = velocityAngle;
|
|
||||||
dataStatic.velocitySpeed = randomInRange3;
|
|
||||||
map.flecks.CreateFleck(dataStatic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTickPosition = ExactPosition;
|
|
||||||
|
|
||||||
// Projectile_Homing_Explosive 的 Tick 逻辑
|
|
||||||
if (HomingDef.isExplosive && HomingDef.explosionDelay > 0)
|
|
||||||
{
|
|
||||||
if (ticksToDetonation > 0)
|
|
||||||
{
|
|
||||||
ticksToDetonation--;
|
|
||||||
if (ticksToDetonation <= 0)
|
|
||||||
{
|
|
||||||
Explode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 exactPosition = ExactPosition;
|
|
||||||
ticksToImpact--;
|
|
||||||
MovementTick();
|
|
||||||
if (!ExactPosition.InBounds(base.Map))
|
|
||||||
{
|
|
||||||
base.Position = exactPosition.ToIntVec3();
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Vector3 exactPosition2 = ExactPosition;
|
|
||||||
object[] parameters = new object[2] { exactPosition, exactPosition2 };
|
|
||||||
if (!(bool)NonPublicFields.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters))
|
|
||||||
{
|
|
||||||
base.Position = ExactPosition.ToIntVec3();
|
|
||||||
if (ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && def.projectile.soundImpactAnticipate != null)
|
|
||||||
{
|
|
||||||
def.projectile.soundImpactAnticipate.PlayOneShot(this);
|
|
||||||
}
|
|
||||||
if (ticksToImpact <= 0)
|
|
||||||
{
|
|
||||||
ImpactSomething();
|
|
||||||
}
|
|
||||||
else if (ambientSustainer != null)
|
|
||||||
{
|
|
||||||
ambientSustainer.Maintain();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Projectile_PoiBullet 原始逻辑中的部分Tick
|
|
||||||
this.tickcount++;
|
|
||||||
bool flag = this.flag3;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
this.CalHit = this.CanHitTarget_Poi(); // 使用重命名后的方法
|
|
||||||
this.flag3 = false;
|
|
||||||
}
|
|
||||||
bool flag2 = !this.CalHit;
|
|
||||||
if (flag2)
|
|
||||||
{
|
|
||||||
this.FindRandCell(this.intendedTarget.CenterVector3);
|
|
||||||
}
|
|
||||||
bool flag3_poi = this.intendedTarget.Thing != null;
|
|
||||||
if (flag3_poi)
|
|
||||||
{
|
|
||||||
this.destination = this.intendedTarget.Thing.DrawPos;
|
|
||||||
}
|
|
||||||
this.Fleck_MakeFleckTick_Poi++; // 使用重命名后的字段
|
|
||||||
bool flag4 = this.Fleck_MakeFleckTick_Poi >= this.Fleck_MakeFleckTickMax_Poi; // 使用重命名后的字段
|
|
||||||
bool flag5 = flag4 && this.tickcount >= 8;
|
|
||||||
if (flag5)
|
|
||||||
{
|
|
||||||
this.Fleck_MakeFleckTick_Poi = 0;
|
|
||||||
Map map = base.Map;
|
|
||||||
int randomInRange = this.Fleck_MakeFleckNum_Poi.RandomInRange;
|
|
||||||
Vector3 vector = this.BPos(base.DistanceCoveredFraction - 0.01f);
|
|
||||||
Vector3 vector2 = this.BPos(base.DistanceCoveredFraction - 0.02f);
|
|
||||||
for (int i = 0; i < randomInRange; i++)
|
|
||||||
{
|
|
||||||
float num = (vector - this.intendedTarget.CenterVector3).AngleFlat();
|
|
||||||
float velocityAngle = this.Fleck_Angle_Poi.RandomInRange + num;
|
|
||||||
float randomInRange2 = this.Fleck_Scale_Poi.RandomInRange;
|
|
||||||
float randomInRange3 = this.Fleck_Speed_Poi.RandomInRange;
|
|
||||||
float randomInRange4 = this.Fleck_Speed2_Poi.RandomInRange;
|
|
||||||
FleckCreationData dataStatic = FleckMaker.GetDataStatic(vector, map, this.FleckDef_Poi, randomInRange2);
|
|
||||||
FleckCreationData dataStatic2 = FleckMaker.GetDataStatic(vector2, map, this.FleckDef2_Poi, randomInRange2);
|
|
||||||
dataStatic.rotation = (vector - vector2).AngleFlat();
|
|
||||||
dataStatic.rotationRate = this.Fleck_Rotation_Poi.RandomInRange;
|
|
||||||
dataStatic.velocityAngle = velocityAngle;
|
|
||||||
dataStatic.velocitySpeed = randomInRange3;
|
|
||||||
dataStatic2.rotation = (vector - vector2).AngleFlat();
|
|
||||||
dataStatic2.rotationRate = this.Fleck_Rotation_Poi.RandomInRange;
|
|
||||||
dataStatic2.velocityAngle = velocityAngle;
|
|
||||||
dataStatic2.velocitySpeed = randomInRange4;
|
|
||||||
map.flecks.CreateFleck(dataStatic2);
|
|
||||||
map.flecks.CreateFleck(dataStatic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 移除原始的 base.Tick(); 因为 Projectile_Homing 的 Tick 已经包含了其父类的逻辑
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThingWithCompsTick()
|
|
||||||
{
|
|
||||||
if (comps != null)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (int count = comps.Count; i < count; i++)
|
|
||||||
{
|
|
||||||
comps[i].CompTick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Impact(Thing hitThing, bool blockedByShield = false)
|
|
||||||
{
|
|
||||||
Map map = base.Map;
|
|
||||||
IntVec3 position = base.Position;
|
|
||||||
|
|
||||||
// Projectile_Homing_Explosive 的 Impact 逻辑
|
|
||||||
if (HomingDef.isExplosive)
|
|
||||||
{
|
|
||||||
bool flag = blockedByShield || HomingDef.explosionDelay == 0;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
Explode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
landed = true;
|
|
||||||
ticksToDetonation = HomingDef.explosionDelay;
|
|
||||||
GenExplosion.NotifyNearbyPawnsOfDangerousExplosive(this, def.projectile.damageDef, launcher.Faction, launcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Projectile_Homing 的 Impact 逻辑
|
|
||||||
{
|
|
||||||
base.Impact(hitThing, blockedByShield);
|
|
||||||
if (HomingDef.extraProjectile != null)
|
|
||||||
{
|
|
||||||
if (hitThing != null && hitThing.Spawned)
|
|
||||||
{
|
|
||||||
((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
((Projectile)GenSpawn.Spawn(HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(launcher, ExactPosition, position, position, ProjectileHitFlags.All, false, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Projectile_PoiBullet 原始逻辑中的 Impact
|
|
||||||
bool flag_poi = this.intendedTarget.Thing is Pawn;
|
|
||||||
if (flag_poi)
|
|
||||||
{
|
|
||||||
hitThing = this.intendedTarget.Thing;
|
|
||||||
}
|
|
||||||
// 原始的 base.Impact(hitThing, blockedByShield); 已经被上面的 Homing 和 Explosive 逻辑覆盖,需要确保正确调用或移除
|
|
||||||
// 这里我们已经调用了 base.Impact(hitThing, blockedByShield); 在 Projectile_Homing 的 Impact 逻辑中,所以这里不再重复调用。
|
|
||||||
|
|
||||||
BattleLogEntry_RangedImpact battleLogEntry_RangedImpact = new BattleLogEntry_RangedImpact(this.launcher, hitThing, this.intendedTarget.Thing, this.equipmentDef, this.def, this.targetCoverDef);
|
|
||||||
Find.BattleLog.Add(battleLogEntry_RangedImpact);
|
|
||||||
this.NotifyImpact_Poi(hitThing, map, position); // 使用重命名后的方法
|
|
||||||
bool flag2 = hitThing != null && !blockedByShield;
|
|
||||||
if (flag2)
|
|
||||||
{
|
|
||||||
Pawn pawn;
|
|
||||||
bool instigatorGuilty = (pawn = (this.launcher as Pawn)) == null || !pawn.Drafted;
|
|
||||||
DamageInfo dinfo = new DamageInfo(this.def.projectile.damageDef, (float)this.DamageAmount, this.ArmorPenetration, this.ExactRotation.eulerAngles.y, this.launcher, null, this.equipmentDef, DamageInfo.SourceCategory.ThingOrUnknown, this.intendedTarget.Thing, instigatorGuilty, true, QualityCategory.Normal, true);
|
|
||||||
hitThing.TakeDamage(dinfo).AssociateWithLog(battleLogEntry_RangedImpact);
|
|
||||||
Pawn pawn2 = hitThing as Pawn;
|
|
||||||
bool flag3 = pawn2 != null && pawn2.stances != null;
|
|
||||||
if (flag3)
|
|
||||||
{
|
|
||||||
pawn2.stances.stagger.Notify_BulletImpact(this);
|
|
||||||
}
|
|
||||||
bool flag4 = this.def.projectile.extraDamages != null;
|
|
||||||
if (flag4)
|
|
||||||
{
|
|
||||||
foreach (ExtraDamage extraDamage in this.def.projectile.extraDamages)
|
|
||||||
{
|
|
||||||
bool flag5 = Rand.Chance(extraDamage.chance);
|
|
||||||
if (flag5)
|
|
||||||
{
|
|
||||||
DamageInfo dinfo2 = new DamageInfo(extraDamage.def, extraDamage.amount, extraDamage.AdjustedArmorPenetration(), this.ExactRotation.eulerAngles.y, this.launcher, null, this.equipmentDef, DamageInfo.SourceCategory.ThingOrUnknown, this.intendedTarget.Thing, instigatorGuilty, true, QualityCategory.Normal, true);
|
|
||||||
hitThing.TakeDamage(dinfo2).AssociateWithLog(battleLogEntry_RangedImpact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool flag6 = Rand.Chance(this.def.projectile.bulletChanceToStartFire) && (pawn2 == null || Rand.Chance(FireUtility.ChanceToAttachFireFromEvent(pawn2)));
|
|
||||||
if (flag6)
|
|
||||||
{
|
|
||||||
hitThing.TryAttachFire(this.def.projectile.bulletFireSizeRange.RandomInRange, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool flag7 = !blockedByShield;
|
|
||||||
if (flag7)
|
|
||||||
{
|
|
||||||
SoundDefOf.BulletImpact_Ground.PlayOneShot(new TargetInfo(base.Position, map, false));
|
|
||||||
bool takeSplashes = base.Position.GetTerrain(map).takeSplashes;
|
|
||||||
if (takeSplashes)
|
|
||||||
{
|
|
||||||
FleckMaker.WaterSplash(this.ExactPosition, map, Mathf.Sqrt((float)this.DamageAmount) * 1f, 4f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FleckMaker.Static(this.ExactPosition, map, FleckDefOf.ShotHit_Dirt, 1f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool flag8 = Rand.Chance(this.def.projectile.bulletChanceToStartFire);
|
|
||||||
if (flag8)
|
|
||||||
{
|
|
||||||
FireUtility.TryStartFireIn(base.Position, map, this.def.projectile.bulletFireSizeRange.RandomInRange, this, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Explode()
|
|
||||||
{
|
|
||||||
Map map = base.Map;
|
|
||||||
ModExtension_Cone modExtension = this.def.GetModExtension<ModExtension_Cone>();
|
|
||||||
DoExplosion();
|
|
||||||
if (modExtension != null)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (this.def.projectile.explosionEffect != null)
|
|
||||||
{
|
|
||||||
Effecter effecter = this.def.projectile.explosionEffect.Spawn();
|
|
||||||
if (this.def.projectile.explosionEffectLifetimeTicks != 0)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ExposeData()
|
|
||||||
{
|
|
||||||
base.ExposeData();
|
|
||||||
Scribe_Values.Look(ref exactPositionInt, "exactPosition");
|
|
||||||
Scribe_Values.Look(ref curSpeed, "curSpeed");
|
|
||||||
Scribe_Values.Look(ref homing, "homing", defaultValue: false);
|
|
||||||
Scribe_Values.Look(ref ticksToDetonation, "ticksToDetonation", 0, false); // 爆炸弹字段
|
|
||||||
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
|
||||||
{
|
|
||||||
ReflectInit();
|
|
||||||
if (this.homingDefInt == null)
|
|
||||||
{
|
|
||||||
this.homingDefInt = this.def.GetModExtension<HomingProjectileDef>();
|
|
||||||
if (this.homingDefInt == null)
|
|
||||||
{
|
|
||||||
Log.ErrorOnce($"HomingProjectileDef is null for projectile {this.def.defName} after PostLoadInit. Creating a default instance.", this.thingIDNumber ^ 0x12345678);
|
|
||||||
this.homingDefInt = new HomingProjectileDef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Projectile_PoiBullet 原始逻辑
|
|
||||||
private void RandFactor()
|
|
||||||
{
|
|
||||||
FloatRange floatRange = new FloatRange(-0.5f, 0.5f);
|
|
||||||
FloatRange floatRange2 = new FloatRange(-0.5f, 0.5f);
|
|
||||||
this.Randdd.x = floatRange.RandomInRange;
|
|
||||||
this.Randdd.z = floatRange2.RandomInRange;
|
|
||||||
this.flag2 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3 BPos(float t)
|
|
||||||
{
|
|
||||||
bool flag = !this.flag2;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
this.RandFactor();
|
|
||||||
}
|
|
||||||
Vector3 origin = this.origin;
|
|
||||||
Vector3 a = (this.origin + this.destination) / 2f;
|
|
||||||
a += this.Randdd;
|
|
||||||
a.y = this.destination.y;
|
|
||||||
Vector3 destination = this.destination;
|
|
||||||
return (1f - t) * (1f - t) * origin + 2f * t * (1f - t) * a + t * t * destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FindRandCell(Vector3 d)
|
|
||||||
{
|
|
||||||
IntVec3 center = IntVec3.FromVector3(d);
|
|
||||||
this.intendedTarget = CellRect.CenteredOn(center, 2).RandomCell;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DrawAt(Vector3 position, bool flip = false)
|
|
||||||
{
|
|
||||||
Vector3 b = this.BPos(base.DistanceCoveredFraction - 0.01f);
|
|
||||||
position = this.BPos(base.DistanceCoveredFraction);
|
|
||||||
Quaternion rotation = Quaternion.LookRotation(position - b);
|
|
||||||
bool flag = this.tickcount >= 4;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
Vector3 position2 = position;
|
|
||||||
position2.y = AltitudeLayer.Projectile.AltitudeFor();
|
|
||||||
Graphics.DrawMesh(MeshPool.GridPlane(this.def.graphicData.drawSize), position2, rotation, this.DrawMat, 0);
|
|
||||||
base.Comps_PostDraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanHitTarget_Poi() // 重命名以避免冲突
|
|
||||||
{
|
|
||||||
bool flag = this.launcher is Pawn;
|
|
||||||
bool result;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
float num = this.Hitchance_Poi(); // 使用重命名后的方法
|
|
||||||
bool flag2 = (float)Rand.RangeInclusive(0, 100) <= num * 100f;
|
|
||||||
Pawn pawn = this.intendedTarget.Thing as Pawn;
|
|
||||||
bool flag3 = pawn != null;
|
|
||||||
if (flag3)
|
|
||||||
{
|
|
||||||
bool downed = pawn.Downed;
|
|
||||||
if (downed)
|
|
||||||
{
|
|
||||||
flag2 = (Rand.RangeInclusive(0, 100) <= 30);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = flag2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = (Rand.RangeInclusive(0, 100) <= 85);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Hitchance_Poi() // 重命名以避免冲突
|
|
||||||
{
|
|
||||||
Pawn pawn = this.launcher as Pawn;
|
|
||||||
bool flag = pawn != null;
|
|
||||||
float result;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
SkillDef named = DefDatabase<SkillDef>.GetNamed("Intellectual", true);
|
|
||||||
SkillRecord skill = pawn.skills.GetSkill(named);
|
|
||||||
bool flag2 = skill != null;
|
|
||||||
if (flag2)
|
|
||||||
{
|
|
||||||
int level = skill.GetLevel(true);
|
|
||||||
float num = Mathf.Min(1f, (float)level * 0.05f);
|
|
||||||
result = num;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = 0.2f;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NotifyImpact_Poi(Thing hitThing, Map map, IntVec3 position) // 重命名以避免冲突
|
|
||||||
{
|
|
||||||
BulletImpactData impactData = new BulletImpactData
|
|
||||||
{
|
|
||||||
bullet = this,
|
|
||||||
hitThing = hitThing,
|
|
||||||
impactPosition = position
|
|
||||||
};
|
|
||||||
bool flag = hitThing != null;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
hitThing.Notify_BulletImpactNearby(impactData);
|
|
||||||
}
|
|
||||||
int num = 9;
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
IntVec3 c = position + GenRadial.RadialPattern[i];
|
|
||||||
bool flag2 = c.InBounds(map);
|
|
||||||
if (flag2)
|
|
||||||
{
|
|
||||||
List<Thing> thingList = c.GetThingList(map);
|
|
||||||
for (int j = 0; j < thingList.Count; j++)
|
|
||||||
{
|
|
||||||
bool flag3 = thingList[j] != hitThing;
|
|
||||||
if (flag3)
|
|
||||||
{
|
|
||||||
thingList[j].Notify_BulletImpactNearby(impactData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool flag2 = false;
|
|
||||||
private bool flag3 = true;
|
|
||||||
private bool CalHit = false;
|
|
||||||
private Vector3 Randdd;
|
|
||||||
private int tickcount;
|
|
||||||
|
|
||||||
// Projectile_PoiBullet 原始的 Fleck 字段,重命名以避免冲突
|
|
||||||
public FleckDef FleckDef_Poi = DefDatabase<FleckDef>.GetNamed("CMC_SparkFlash_Blue_Small", true);
|
|
||||||
public FleckDef FleckDef2_Poi = DefDatabase<FleckDef>.GetNamed("CMC_SparkFlash_Blue_LongLasting_Small", true);
|
|
||||||
public int Fleck_MakeFleckTickMax_Poi = 1;
|
|
||||||
public IntRange Fleck_MakeFleckNum_Poi = new IntRange(2, 2);
|
|
||||||
public FloatRange Fleck_Angle_Poi = new FloatRange(-180f, 180f);
|
|
||||||
public FloatRange Fleck_Scale_Poi = new FloatRange(1.6f, 1.7f);
|
|
||||||
public FloatRange Fleck_Speed_Poi = new FloatRange(5f, 7f);
|
|
||||||
public FloatRange Fleck_Speed2_Poi = new FloatRange(0.1f, 0.2f);
|
|
||||||
public FloatRange Fleck_Rotation_Poi = new FloatRange(-180f, 180f);
|
|
||||||
public int Fleck_MakeFleckTick_Poi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -124,14 +124,10 @@
|
|||||||
<Compile Include="NeedDefExtension_Energy.cs" />
|
<Compile Include="NeedDefExtension_Energy.cs" />
|
||||||
<Compile Include="Projectile_WulaPenetrating.cs" />
|
<Compile Include="Projectile_WulaPenetrating.cs" />
|
||||||
<Compile Include="Projectile_WulaBeam.cs" />
|
<Compile Include="Projectile_WulaBeam.cs" />
|
||||||
<Compile Include="Projectile_Homing.cs" />
|
|
||||||
<Compile Include="Projectile_Homing_Explosive.cs" />
|
|
||||||
<Compile Include="Projectile_TrackingBullet.cs" />
|
<Compile Include="Projectile_TrackingBullet.cs" />
|
||||||
<Compile Include="TrackingBulletDef.cs" />
|
<Compile Include="TrackingBulletDef.cs" />
|
||||||
<Compile Include="ExplosiveTrackingBulletDef.cs" />
|
<Compile Include="ExplosiveTrackingBulletDef.cs" />
|
||||||
<Compile Include="Projectile_ExplosiveTrackingBullet.cs" />
|
<Compile Include="Projectile_ExplosiveTrackingBullet.cs" />
|
||||||
<Compile Include="ModExtension_Cone.cs" />
|
|
||||||
<Compile Include="HomingProjectileDef.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="PsychicRitual_TechOffering.cs" />
|
<Compile Include="PsychicRitual_TechOffering.cs" />
|
||||||
<Compile Include="PsychicRitualDef_AddHediff.cs" />
|
<Compile Include="PsychicRitualDef_AddHediff.cs" />
|
||||||
@@ -170,11 +166,13 @@
|
|||||||
<Compile Include="WeaponSwitch.cs" />
|
<Compile Include="WeaponSwitch.cs" />
|
||||||
<Compile Include="Verb\CompMultiStrike.cs" />
|
<Compile Include="Verb\CompMultiStrike.cs" />
|
||||||
<Compile Include="Verb\Verb_MeleeAttack_MultiStrike.cs" />
|
<Compile Include="Verb\Verb_MeleeAttack_MultiStrike.cs" />
|
||||||
</ItemGroup>
|
<Compile Include="Projectile_ExplosiveWithTrail.cs" />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Compile Include="BulletWithTrail.cs" />
|
||||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
</ItemGroup>
|
||||||
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<RemoveDir Directories="$(ProjectDir)obj\Debug" />
|
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||||
<RemoveDir Directories="$(ProjectDir)obj\Release" />
|
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
||||||
</Target>
|
<RemoveDir Directories="$(ProjectDir)obj\Debug" />
|
||||||
|
<RemoveDir Directories="$(ProjectDir)obj\Release" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user