This commit is contained in:
2025-08-27 18:28:14 +08:00
parent 5eb865ef3a
commit e1477593a6
7 changed files with 2258 additions and 91 deletions

View File

@@ -1,4 +1,5 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
@@ -13,5 +14,18 @@ namespace WulaFallenEmpire
public float sustainedDamagePerTick = 15f;
public int tickInterval = 10;
public int duration = 120;
// --- NEW: Explosion Path Properties (for both modes) ---
public bool explosionEnabled = false;
public int explosionTickInterval = 15;
public DamageDef explosionDamageDef;
public float explosionEnergyCostRatio = 0.5f; // Only for Breaching Beam
// Manual explosion effect properties
public float explosionHeatEnergyPerCell = 0;
public FleckDef explosionCellFleck;
public Color explosionColorCenter = Color.white;
public Color explosionColorEdge = Color.white;
public SoundDef soundExplosion;
}
}

View File

@@ -18,7 +18,9 @@ namespace WulaFallenEmpire
// --- Our custom state ---
private Vector3 beamEndPoint;
private int ticksLeft;
private bool beamHitMapEdge; // NEW: Flag to check if the beam reached the map edge
private bool beamHitMapEdge;
private int explosionTicks;
private float beamEnergy;
private VerbProperties_Wula_IonicBeam BeamProps => (VerbProperties_Wula_IonicBeam)verbProps;
@@ -28,14 +30,14 @@ namespace WulaFallenEmpire
{
base.WarmupComplete();
// --- Custom Damage Logic ---
beamHitMapEdge = true; // Assume it will hit the edge unless stopped
// --- Initial Damage and Path Calculation ---
beamHitMapEdge = true;
float shotAngle = (currentTarget.Cell - caster.Position).AngleFlat;
beamEndPoint = GetMapEdgePoint(caster.Position, shotAngle);
var cellsOnPath = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEndPoint.ToIntVec3(), verbProps.beamWidth);
var beamEnergy = BeamProps.breachingDamage; // Local variable for calculation
var cellsOnPath = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEndPoint.ToIntVec3(), (int)verbProps.beamWidth);
this.beamEnergy = BeamProps.breachingDamage;
// This loop calculates the final beam end point based on energy depletion
// This loop calculates the final beam end point based on the initial piercing damage
foreach (var cell in cellsOnPath)
{
if (!cell.InBounds(caster.Map)) continue;
@@ -46,7 +48,7 @@ namespace WulaFallenEmpire
if (beamEnergy <= 0) break;
float damageToDeal = Mathf.Min(beamEnergy, thing.HitPoints);
var dinfo = new DamageInfo(verbProps.beamDamageDef ?? DamageDefOf.Burn, damageToDeal, BeamProps.armorPenetration, shotAngle, caster, EquipmentSource);
var dinfo = new DamageInfo(verbProps.beamDamageDef ?? DamageDefOf.Burn, damageToDeal, BeamProps.armorPenetration, shotAngle, caster, null, EquipmentSource?.def);
thing.TakeDamage(dinfo);
beamEnergy -= thing.HitPoints;
@@ -54,13 +56,13 @@ namespace WulaFallenEmpire
if (beamEnergy <= 0)
{
beamEndPoint = cell.ToVector3Shifted(); // The beam stops here
beamHitMapEdge = false; // It was stopped, so it didn't hit the edge
beamEndPoint = cell.ToVector3Shifted();
beamHitMapEdge = false;
break;
}
}
// --- Copied Effect Logic ---
// --- Start Visual Effects ---
if (verbProps.beamMoteDef != null)
{
mote = MoteMaker.MakeInteractionOverlay(verbProps.beamMoteDef, caster, new TargetInfo(beamEndPoint.ToIntVec3(), caster.Map));
@@ -75,7 +77,7 @@ namespace WulaFallenEmpire
{
if (ticksLeft > 0)
{
// --- Copied Effect Logic ---
// --- Maintain Visual Effects ---
if (mote != null)
{
mote.UpdateTargets(new TargetInfo(caster.Position, caster.Map), new TargetInfo(beamEndPoint.ToIntVec3(), caster.Map), Vector3.zero, Vector3.zero);
@@ -91,6 +93,17 @@ namespace WulaFallenEmpire
}
sustainer?.Maintain();
// --- Path Explosion Logic ---
if (BeamProps.explosionEnabled)
{
explosionTicks--;
if (explosionTicks <= 0)
{
ApplyPathExplosionDamage();
explosionTicks = BeamProps.explosionTickInterval;
}
}
ticksLeft--;
if (ticksLeft <= 0)
{
@@ -101,18 +114,18 @@ namespace WulaFallenEmpire
protected override bool TryCastShot()
{
// The actual "shot" is just starting the effects, damage is pre-calculated in WarmupComplete
this.state = VerbState.Bursting;
// NEW: Set duration based on whether it hit the map edge
if (beamHitMapEdge)
{
this.ticksLeft = BeamProps.breachingBeamDuration;
}
else
{
this.ticksLeft = 1; // Disappears almost instantly if blocked
this.ticksLeft = 1;
}
this.explosionTicks = 0;
return true;
}
@@ -125,12 +138,49 @@ namespace WulaFallenEmpire
sustainer?.End();
}
private void ApplyPathExplosionDamage()
{
if (this.beamEnergy <= 0 || BeamProps.explosionDamageDef == null) return;
var pathCells = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEndPoint.ToIntVec3(), (int)verbProps.beamWidth);
var shotAngle = (beamEndPoint - caster.DrawPos).AngleFlat();
var explosionDamageDef = BeamProps.explosionDamageDef;
foreach (var cell in pathCells)
{
if (this.beamEnergy <= 0) break;
if (!cell.InBounds(caster.Map)) continue;
// Performance optimization: don't create explosions on every single cell of the path
if (cell.GetHashCode() % 2 != 0) continue;
var thingsToHit = cell.GetThingList(caster.Map).Where(t => CanHit(t)).ToList();
foreach (var thing in thingsToHit)
{
if (this.beamEnergy <= 0) break;
var dinfo = new DamageInfo(explosionDamageDef, explosionDamageDef.defaultDamage, explosionDamageDef.defaultArmorPenetration, shotAngle, caster, null, EquipmentSource?.def);
float damageDealt = Mathf.Min(thing.HitPoints, dinfo.Amount);
thing.TakeDamage(dinfo);
this.beamEnergy -= damageDealt * BeamProps.explosionEnergyCostRatio;
}
if(explosionDamageDef?.explosionCellMote != null)
{
FleckMaker.Static(cell, caster.Map, explosionDamageDef.explosionCellMote);
}
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref beamEndPoint, "beamEndPoint");
Scribe_Values.Look(ref ticksLeft, "ticksLeft");
Scribe_Values.Look(ref beamHitMapEdge, "beamHitMapEdge");
Scribe_Values.Look(ref explosionTicks, "explosionTicks");
Scribe_Values.Look(ref beamEnergy, "beamEnergy");
}
private bool CanHit(Thing t)

View File

@@ -17,8 +17,9 @@ namespace WulaFallenEmpire
// --- Our custom state ---
private int ticksLeft;
private int ticksToNextDamage;
private int explosionTicks;
private Vector3 beamEnd;
private VerbProperties_Wula_IonicBeam BeamProps => (VerbProperties_Wula_IonicBeam)verbProps;
public override float? AimAngleOverride => (state == VerbState.Bursting) ? (beamEnd - caster.DrawPos).AngleFlat() : (float?)null;
@@ -27,11 +28,9 @@ namespace WulaFallenEmpire
{
base.WarmupComplete();
// For sustained beam, it always reaches its max range
var shotAngle = (currentTarget.Cell - caster.Position).AngleFlat;
beamEnd = GetMapEdgePoint(caster.Position, shotAngle);
// --- Copied Effect Logic ---
if (verbProps.beamMoteDef != null)
{
mote = MoteMaker.MakeInteractionOverlay(verbProps.beamMoteDef, caster, new TargetInfo(beamEnd.ToIntVec3(), caster.Map));
@@ -44,10 +43,9 @@ namespace WulaFallenEmpire
public override void BurstingTick()
{
// This verb is not a standard "burst", but we use the state to manage the effect
if (ticksLeft > 0)
{
// --- Copied Effect Logic ---
// --- Maintain Visual Effects ---
if (mote != null)
{
mote.UpdateTargets(new TargetInfo(caster.Position, caster.Map), new TargetInfo(beamEnd.ToIntVec3(), caster.Map), Vector3.zero, Vector3.zero);
@@ -63,15 +61,26 @@ namespace WulaFallenEmpire
}
sustainer?.Maintain();
// --- Custom Damage Logic ---
ticksLeft--;
// --- Beam Damage Logic ---
ticksToNextDamage--;
if (ticksToNextDamage <= 0)
{
ApplyDamage();
ApplyBeamDamage();
ticksToNextDamage = BeamProps.tickInterval;
}
// --- Path Explosion Logic ---
if (BeamProps.explosionEnabled)
{
explosionTicks--;
if (explosionTicks <= 0)
{
ApplyPathExplosionDamage();
explosionTicks = BeamProps.explosionTickInterval;
}
}
ticksLeft--;
if (ticksLeft <= 0)
{
StopBeam();
@@ -83,16 +92,25 @@ namespace WulaFallenEmpire
{
this.state = VerbState.Bursting;
this.ticksLeft = BeamProps.duration;
this.ticksToNextDamage = 0; // First damage tick happens immediately
this.ticksToNextDamage = 0;
this.explosionTicks = 0;
return true;
}
private void ApplyDamage()
private void StopBeam()
{
this.state = VerbState.Idle;
mote?.Destroy();
endEffecter?.Cleanup();
sustainer?.End();
}
private void ApplyBeamDamage()
{
var shotAngle = (beamEnd - caster.DrawPos).AngleFlat();
var dinfo = new DamageInfo(verbProps.beamDamageDef ?? DamageDefOf.Burn, BeamProps.sustainedDamagePerTick, BeamProps.armorPenetration, shotAngle, caster, EquipmentSource);
var cellsInBeam = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEnd.ToIntVec3(), verbProps.beamWidth);
var dinfo = new DamageInfo(verbProps.beamDamageDef ?? DamageDefOf.Burn, BeamProps.sustainedDamagePerTick, BeamProps.armorPenetration, shotAngle, caster, null, EquipmentSource?.def);
var cellsInBeam = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEnd.ToIntVec3(), (int)verbProps.beamWidth);
foreach (var cell in cellsInBeam)
{
@@ -105,13 +123,38 @@ namespace WulaFallenEmpire
}
}
}
private void StopBeam()
private void ApplyPathExplosionDamage()
{
this.state = VerbState.Idle;
mote?.Destroy();
endEffecter?.Cleanup();
sustainer?.End();
if (BeamProps.explosionDamageDef == null) return;
var pathCells = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEnd.ToIntVec3(), (int)verbProps.beamWidth);
var shotAngle = (beamEnd - caster.DrawPos).AngleFlat();
var explosionDamageDef = BeamProps.explosionDamageDef;
foreach (var cell in pathCells)
{
if (!cell.InBounds(caster.Map)) continue;
if (cell.GetHashCode() % 3 != 0) continue;
var thingsToHit = cell.GetThingList(caster.Map).Where(t => CanHit(t)).ToList();
foreach (var thing in thingsToHit)
{
var dinfo = new DamageInfo(explosionDamageDef, explosionDamageDef.defaultDamage, explosionDamageDef.defaultArmorPenetration, shotAngle, caster, null, EquipmentSource?.def);
thing.TakeDamage(dinfo);
}
if(BeamProps.explosionCellFleck != null)
{
FleckMaker.Static(cell, caster.Map, BeamProps.explosionCellFleck);
}
if (BeamProps.soundExplosion != null)
{
BeamProps.soundExplosion.PlayOneShot(new TargetInfo(cell, caster.Map));
}
GenTemperature.PushHeat(cell, caster.Map, BeamProps.explosionHeatEnergyPerCell);
}
}
public override void ExposeData()
@@ -119,6 +162,7 @@ namespace WulaFallenEmpire
base.ExposeData();
Scribe_Values.Look(ref ticksLeft, "ticksLeft", 0);
Scribe_Values.Look(ref ticksToNextDamage, "ticksToNextDamage", 0);
Scribe_Values.Look(ref explosionTicks, "explosionTicks");
Scribe_Values.Look(ref beamEnd, "beamEnd");
}
@@ -131,7 +175,7 @@ namespace WulaFallenEmpire
{
float mapSize = Mathf.Max(caster.Map.Size.x, caster.Map.Size.z) * 1.5f;
Vector3 direction = Quaternion.AngleAxis(angle, Vector3.up) * Vector3.forward;
return start.toVector3() + direction * mapSize;
return start.ToVector3() + direction * mapSize;
}
}
}

View File

@@ -13,12 +13,12 @@ namespace WulaFallenEmpire
// A more advanced method to get all cells in a rectangular area
public static IEnumerable<IntVec3> GetCellsInBeamArea(IntVec3 start, IntVec3 end, int width)
{
var beamLine = GenSight.PointsOnLineOfSight(start, end);
if (width <= 1)
{
return GenGrid.PointsOnLine(start, end).Distinct();
return beamLine.Distinct();
}
var beamLine = GenGrid.PointsOnLine(start, end).ToList();
var allCells = new HashSet<IntVec3>(beamLine);
var halfWidth = (width - 1) / 2;

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -185,11 +186,15 @@
<Compile Include="Hediff_DamageShield.cs" />
<Compile Include="DRMDamageShield.cs" />
<Compile Include="CompUseEffect_AddDamageShieldCharges.cs" />
<Compile Include="HediffCompProperties_GiveHediffsInRangeToRace.cs" />
<Compile Include="HediffCompProperties_GiveHediffsInRangeToRace.cs" />
<Compile Include="HediffComp_GiveHediffsInRangeToRace.cs" />
<Compile Include="CompForceTargetable.cs" />
<Compile Include="Patch_ForceTargetable.cs" />
<Compile Include="Patch_ArmedShuttle_ForceTargetable.cs" />
<Compile Include="VerbProperties_Wula_IonicBeam.cs" />
<Compile Include="Verb_Wula_BreachingBeam.cs" />
<Compile Include="Verb_Wula_SustainedBeam.cs" />
<Compile Include="WulaBeamUtility.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />