暂存
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<ThingDef ParentName="MoteBase">
|
||||
<defName>ExcaliburBeam</defName>
|
||||
<thingClass>WulaFallenEmpire.Thing_ExcaliburBeam</thingClass>
|
||||
<label>Excalibur Beam</label>
|
||||
<altitudeLayer>MoteOverhead</altitudeLayer>
|
||||
<graphicData>
|
||||
<texPath>Things/Mote/PowerBeam</texPath>
|
||||
<shaderType>MoteGlow</shaderType>
|
||||
</graphicData>
|
||||
<drawOffscreen>true</drawOffscreen>
|
||||
<mote>
|
||||
<fadeInTime>0.5</fadeInTime>
|
||||
<solidTime>9.3</solidTime>
|
||||
<fadeOutTime>1.0</fadeOutTime>
|
||||
</mote>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
@@ -6,7 +6,7 @@
|
||||
离子武器最终正确架构配置示例 (V17.0)
|
||||
=====================================================================
|
||||
说明:
|
||||
- 核心架构: 自定义 VerbProperties (VerbProperties_Wula_IonicBeam)
|
||||
- 核心架构: 自定义 VerbProperties (VerbProperties_Excalibur)
|
||||
- 伤害逻辑: 完全在C#中自定义,包括路径伤害和能量消耗。
|
||||
- 特效逻辑: 主要由我们自己在VerbProperties中定义的参数手动控制。
|
||||
-->
|
||||
@@ -28,90 +28,56 @@
|
||||
<AccuracyLong>1</AccuracyLong>
|
||||
<RangedWeapon_Cooldown>1.5</RangedWeapon_Cooldown>
|
||||
</statBases>
|
||||
<weaponTags>
|
||||
<li>WulaExcalibur</li>
|
||||
</weaponTags>
|
||||
</ThingDef>
|
||||
|
||||
<!-- ==================== 模式一: 离子突破光束枪 (爆发贯穿 + 路径爆炸) ==================== -->
|
||||
|
||||
<ThingDef ParentName="Wula_BaseIonicGun">
|
||||
<defName>WULA_Weapon_BreachingBeamGun</defName>
|
||||
<label>离子突破光束枪</label>
|
||||
<description>发射一道高能离子束,能够烧穿路径上的多个目标,直到能量耗尽。光束路径上会周期性地引发小规模湮灭反应。</description>
|
||||
<verbs>
|
||||
<li Class="WulaFallenEmpire.VerbProperties_Wula_IonicBeam">
|
||||
<verbClass>WulaFallenEmpire.Verb_Wula_BreachingBeam</verbClass>
|
||||
|
||||
<!-- 基础参数 -->
|
||||
<hasStandardCommand>true</hasStandardCommand>
|
||||
<warmupTime>2.5</warmupTime>
|
||||
<range>40</range>
|
||||
<burstShotCount>1</burstShotCount>
|
||||
<soundCast>BeamGraser_Shooting</soundCast>
|
||||
|
||||
<!-- 我们自定义的伤害参数 -->
|
||||
<breachingDamage>300</breachingDamage>
|
||||
<armorPenetration>0.95</armorPenetration>
|
||||
<breachingBeamDuration>45</breachingBeamDuration>
|
||||
|
||||
<!-- 路径爆炸参数 -->
|
||||
<explosionEnabled>true</explosionEnabled>
|
||||
<explosionTickInterval>10</explosionTickInterval>
|
||||
<explosionDamageDef>Wula_Dark_Matter</explosionDamageDef>
|
||||
<explosionEnergyCostRatio>0.1</explosionEnergyCostRatio>
|
||||
|
||||
<!-- 手动特效参数 -->
|
||||
<explosionHeatEnergyPerCell>10</explosionHeatEnergyPerCell>
|
||||
<explosionCellFleck>BlastFlame</explosionCellFleck>
|
||||
<soundExplosion>Explosion_Flame</soundExplosion>
|
||||
|
||||
<!-- 光束视觉特效参数 -->
|
||||
<soundCastBeam>BeamGraser_Shooting</soundCastBeam>
|
||||
<muzzleFlashScale>12</muzzleFlashScale>
|
||||
<beamWidth>3</beamWidth>
|
||||
<beamMoteDef>Mote_GraserBeamBase</beamMoteDef>
|
||||
<beamEndEffecterDef>GraserBeam_End</beamEndEffecterDef>
|
||||
<defName>WULA_Weapon_Excalibur</defName>
|
||||
<label>王者之剑</label>
|
||||
<description>一把传奇的剑,能够释放出沿路径爆炸的能量。</description>
|
||||
<graphicData>
|
||||
<texPath>Wula/Weapon/WULA_RW_DM_AR</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</graphicData>
|
||||
<uiIconScale>1.5</uiIconScale>
|
||||
<equippedAngleOffset>45</equippedAngleOffset>
|
||||
<tools>
|
||||
<li>
|
||||
<label>剑刃</label>
|
||||
<capacities>
|
||||
<li>Cut</li>
|
||||
</capacities>
|
||||
<power>15</power>
|
||||
<cooldownTime>2</cooldownTime>
|
||||
</li>
|
||||
</verbs>
|
||||
</ThingDef>
|
||||
|
||||
<!-- ==================== 模式二: 离子灼烧光束枪 (持续伤害 + 路径爆炸) ==================== -->
|
||||
|
||||
<ThingDef ParentName="Wula_BaseIonicGun">
|
||||
<defName>WULA_Weapon_SustainedBeamGun</defName>
|
||||
<label>离子灼烧光束枪</label>
|
||||
<description>投射一道持续存在的离子场,对作用范围内的所有敌人进行周期性灼烧,并引发连续的能量爆炸。</description>
|
||||
</tools>
|
||||
<verbs>
|
||||
<li Class="WulaFallenEmpire.VerbProperties_Wula_IonicBeam">
|
||||
<verbClass>WulaFallenEmpire.Verb_Wula_SustainedBeam</verbClass>
|
||||
|
||||
<!-- 基础参数 -->
|
||||
<li Class="WulaFallenEmpire.VerbProperties_Excalibur">
|
||||
<verbClass>WulaFallenEmpire.Verb_Excalibur</verbClass>
|
||||
<hasStandardCommand>true</hasStandardCommand>
|
||||
<range>25.9</range>
|
||||
<warmupTime>1.5</warmupTime>
|
||||
<range>30</range>
|
||||
|
||||
<!-- 我们自定义的伤害参数 -->
|
||||
<sustainedDamagePerTick>15</sustainedDamagePerTick>
|
||||
<tickInterval>15</tickInterval>
|
||||
<duration>240</duration>
|
||||
<armorPenetration>0.5</armorPenetration>
|
||||
|
||||
<!-- 路径爆炸参数 -->
|
||||
<explosionEnabled>true</explosionEnabled>
|
||||
<explosionTickInterval>25</explosionTickInterval>
|
||||
<explosionDamageDef>Wula_Dark_Matter_Flame</explosionDamageDef>
|
||||
|
||||
<!-- 手动特效参数 -->
|
||||
<explosionHeatEnergyPerCell>10</explosionHeatEnergyPerCell>
|
||||
<explosionCellFleck>BlastFlame</explosionCellFleck>
|
||||
<soundExplosion>Explosion_Flame</soundExplosion>
|
||||
|
||||
<!-- 光束视觉特效参数 -->
|
||||
<soundCastBeam>BeamGraser_Shooting</soundCastBeam>
|
||||
<muzzleFlashScale>9</muzzleFlashScale>
|
||||
<beamWidth>3</beamWidth>
|
||||
<beamMoteDef>Mote_GraserBeamBase</beamMoteDef>
|
||||
<beamEndEffecterDef>GraserBeam_End</beamEndEffecterDef>
|
||||
<burstShotCount>10</burstShotCount>
|
||||
<ticksBetweenBurstShots>50</ticksBetweenBurstShots>
|
||||
<soundCast>ChargeLance_Fire</soundCast>
|
||||
<soundCastTail>GunTail_Heavy</soundCastTail>
|
||||
<targetParams>
|
||||
<canTargetLocations>true</canTargetLocations>
|
||||
</targetParams>
|
||||
<accuracyTouch>0.7</accuracyTouch>
|
||||
<accuracyShort>0.6</accuracyShort>
|
||||
<accuracyMedium>0.5</accuracyMedium>
|
||||
<accuracyLong>0.4</accuracyLong>
|
||||
<minRange>3</minRange>
|
||||
<requireLineOfSight>true</requireLineOfSight>
|
||||
<pathWidth>1</pathWidth>
|
||||
<damageDef>Vaporize</damageDef>
|
||||
<damageAmount>50</damageAmount>
|
||||
<armorPenetration>0.3</armorPenetration>
|
||||
</li>
|
||||
</verbs>
|
||||
</ThingDef>
|
||||
</ThingDef>
|
||||
|
||||
</Defs>
|
||||
File diff suppressed because one or more lines are too long
97
Source/WulaFallenEmpire/Thing_ExcaliburBeam.cs
Normal file
97
Source/WulaFallenEmpire/Thing_ExcaliburBeam.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Thing_ExcaliburBeam : Mote
|
||||
{
|
||||
public IntVec3 targetCell;
|
||||
public Pawn caster;
|
||||
public ThingDef weaponDef;
|
||||
public float damageAmount;
|
||||
public float armorPenetration;
|
||||
public float pathWidth;
|
||||
public DamageDef damageDef;
|
||||
|
||||
// Burst shot support
|
||||
public int burstShotsTotal = 1;
|
||||
public int currentBurstShot = 0;
|
||||
|
||||
// Path cells for this burst
|
||||
private List<IntVec3> currentBurstCells;
|
||||
|
||||
private int ticksToDetonate = 0;
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref targetCell, "targetCell");
|
||||
Scribe_References.Look(ref caster, "caster");
|
||||
Scribe_Defs.Look(ref weaponDef, "weaponDef");
|
||||
Scribe_Values.Look(ref damageAmount, "damageAmount");
|
||||
Scribe_Values.Look(ref armorPenetration, "armorPenetration");
|
||||
Scribe_Values.Look(ref pathWidth, "pathWidth");
|
||||
Scribe_Defs.Look(ref damageDef, "damageDef");
|
||||
Scribe_Values.Look(ref burstShotsTotal, "burstShotsTotal", 1);
|
||||
Scribe_Values.Look(ref currentBurstShot, "currentBurstShot", 0);
|
||||
}
|
||||
|
||||
public void StartStrike(List<IntVec3> allCells, int burstIndex, int totalBursts)
|
||||
{
|
||||
currentBurstCells = allCells;
|
||||
currentBurstShot = burstIndex;
|
||||
burstShotsTotal = totalBursts;
|
||||
ticksToDetonate = 1; // Start detonation immediately
|
||||
}
|
||||
|
||||
protected override void TimeInterval(float deltaTime)
|
||||
{
|
||||
base.TimeInterval(deltaTime);
|
||||
if (ticksToDetonate > 0)
|
||||
{
|
||||
ticksToDetonate--;
|
||||
if (ticksToDetonate == 0)
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Detonate()
|
||||
{
|
||||
if (currentBurstCells == null || !currentBurstCells.Any())
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// For this burst, we'll detonate all cells
|
||||
foreach (IntVec3 cell in currentBurstCells)
|
||||
{
|
||||
if (cell.InBounds(Map))
|
||||
{
|
||||
// Apply explosion effect, but ignore the caster
|
||||
List<Thing> ignoredThings = new List<Thing> { caster };
|
||||
DamageDef explosionDamageType = damageDef ?? DamageDefOf.Bomb;
|
||||
GenExplosion.DoExplosion(center: cell, map: Map, radius: 0.9f, damType: explosionDamageType, instigator: caster,
|
||||
damAmount: (int)damageAmount, armorPenetration: armorPenetration,
|
||||
explosionSound: null, weapon: weaponDef, projectile: null,
|
||||
intendedTarget: null, postExplosionSpawnThingDef: null,
|
||||
postExplosionSpawnChance: 0f, postExplosionSpawnThingCount: 1,
|
||||
postExplosionGasType: null, applyDamageToExplosionCellsNeighbors: false,
|
||||
preExplosionSpawnThingDef: null, preExplosionSpawnChance: 0f,
|
||||
preExplosionSpawnThingCount: 1, chanceToStartFire: 0f,
|
||||
damageFalloff: false, direction: null, ignoredThings: ignoredThings,
|
||||
affectedAngle: null, doVisualEffects: true, propagationSpeed: 0f,
|
||||
screenShakeFactor: 0f, doSoundEffects: true, postExplosionSpawnThingDefWater: null,
|
||||
flammabilityChanceCurve: null, overrideCells: null, postExplosionSpawnSingleThingDef: null, preExplosionSpawnSingleThingDef: null);
|
||||
}
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Source/WulaFallenEmpire/Verb/VerbProperties_Excalibur.cs
Normal file
13
Source/WulaFallenEmpire/Verb/VerbProperties_Excalibur.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class VerbProperties_Excalibur : VerbProperties
|
||||
{
|
||||
public float pathWidth = 1f; // Default path width
|
||||
public DamageDef damageDef; // Custom damage type
|
||||
public float damageAmount = -1f; // Custom damage amount
|
||||
public float armorPenetration = -1f; // Custom armor penetration
|
||||
}
|
||||
}
|
||||
201
Source/WulaFallenEmpire/Verb/Verb_Excalibur.cs
Normal file
201
Source/WulaFallenEmpire/Verb/Verb_Excalibur.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Verb_Excalibur : Verb
|
||||
{
|
||||
private new Pawn CasterPawn
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.CasterPawn;
|
||||
}
|
||||
}
|
||||
|
||||
private ThingWithComps weapon
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.CasterPawn.equipment.Primary;
|
||||
}
|
||||
}
|
||||
|
||||
private QualityCategory quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.weapon.TryGetComp<CompQuality>().Quality;
|
||||
}
|
||||
}
|
||||
|
||||
private float damageAmountBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.weapon.def.tools.First<Tool>().power;
|
||||
}
|
||||
}
|
||||
|
||||
private float armorPenetrationBase
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.weapon.def.tools.First<Tool>().armorPenetration;
|
||||
}
|
||||
}
|
||||
|
||||
private float damageAmount
|
||||
{
|
||||
get
|
||||
{
|
||||
// Use the damageAmount from VerbProperties if set, otherwise use the base damage
|
||||
if (this.ExcaliburProps.damageAmount > 0)
|
||||
{
|
||||
return this.ExcaliburProps.damageAmount;
|
||||
}
|
||||
// Removed AncotUtility.QualityFactor, using a simple multiplier for now
|
||||
return 1.0f * this.damageAmountBase;
|
||||
}
|
||||
}
|
||||
|
||||
private float armorPenetration
|
||||
{
|
||||
get
|
||||
{
|
||||
// Use the armorPenetration from VerbProperties if set, otherwise use the base value
|
||||
if (this.ExcaliburProps.armorPenetration >= 0)
|
||||
{
|
||||
return this.ExcaliburProps.armorPenetration;
|
||||
}
|
||||
// Removed AncotUtility.QualityFactor, using a simple multiplier for now
|
||||
return 1.0f * this.armorPenetrationBase;
|
||||
}
|
||||
}
|
||||
|
||||
// Temporarily commented out CompWeaponCharge related code
|
||||
/*
|
||||
public CompWeaponCharge compCharge
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.weapon.TryGetComp<CompWeaponCharge>();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private VerbProperties_Excalibur ExcaliburProps
|
||||
{
|
||||
get
|
||||
{
|
||||
return (VerbProperties_Excalibur)this.verbProps;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryCastShot()
|
||||
{
|
||||
// Temporarily commented out CompWeaponCharge related code
|
||||
/*
|
||||
bool flag = this.compCharge != null && !this.compCharge.CanBeUsed;
|
||||
if (!flag)
|
||||
{
|
||||
CompWeaponCharge compCharge = this.compCharge;
|
||||
if (compCharge != null)
|
||||
{
|
||||
compCharge.UsedOnce();
|
||||
}
|
||||
*/
|
||||
|
||||
// Calculate all affected cells once
|
||||
List<IntVec3> allAffectedCells = this.AffectedCells(this.currentTarget);
|
||||
|
||||
// Create a beam for this specific burst
|
||||
Thing_ExcaliburBeam beam = (Thing_ExcaliburBeam)GenSpawn.Spawn(DefDatabase<ThingDef>.GetNamed("ExcaliburBeam", true), this.CasterPawn.Position, this.CasterPawn.Map);
|
||||
beam.caster = this.CasterPawn;
|
||||
beam.targetCell = this.currentTarget.Cell;
|
||||
beam.damageAmount = this.damageAmount;
|
||||
beam.armorPenetration = this.armorPenetration;
|
||||
beam.pathWidth = this.ExcaliburProps.pathWidth;
|
||||
beam.weaponDef = this.CasterPawn.equipment.Primary.def;
|
||||
beam.damageDef = this.ExcaliburProps.damageDef;
|
||||
beam.StartStrike(allAffectedCells, this.BurstShotsLeft, this.BurstShotCount);
|
||||
|
||||
return true;
|
||||
/*
|
||||
}
|
||||
return false;
|
||||
*/
|
||||
}
|
||||
|
||||
public override void DrawHighlight(LocalTargetInfo target)
|
||||
{
|
||||
GenDraw.DrawFieldEdges(this.AffectedCells(target), 2900);
|
||||
}
|
||||
|
||||
private List<IntVec3> AffectedCells(LocalTargetInfo target)
|
||||
{
|
||||
this.tmpCells.Clear();
|
||||
Vector3 vector = this.CasterPawn.Position.ToVector3Shifted().Yto0();
|
||||
IntVec3 endCell = this.TargetPosition(this.CasterPawn, target);
|
||||
this.tmpCells.Clear();
|
||||
foreach (IntVec3 cell in GenSight.BresenhamCellsBetween(this.CasterPawn.Position, endCell))
|
||||
{
|
||||
if (!cell.InBounds(this.CasterPawn.Map))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (cell.GetEdifice(this.CasterPawn.Map) != null && cell.GetEdifice(this.CasterPawn.Map).def.passability == Traversability.Impassable)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Add cells around the current cell based on pathWidth
|
||||
// Convert pathWidth to proper radius for GenRadial
|
||||
float radius = Math.Max(0.5f, this.ExcaliburProps.pathWidth - 0.5f);
|
||||
foreach (IntVec3 radialCell in GenRadial.RadialCellsAround(cell, radius, true))
|
||||
{
|
||||
if (radialCell.InBounds(this.CasterPawn.Map) && !this.tmpCells.Contains(radialCell))
|
||||
{
|
||||
this.tmpCells.Add(radialCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.tmpCells;
|
||||
}
|
||||
|
||||
public IntVec3 TargetPosition(Pawn pawn, LocalTargetInfo currentTarget)
|
||||
{
|
||||
IntVec3 position = pawn.Position;
|
||||
IntVec3 cell = currentTarget.Cell;
|
||||
Vector3 direction = (cell - position).ToVector3().normalized;
|
||||
|
||||
// Define a maximum range to prevent infinite loops or excessively long beams
|
||||
float maxRange = 1000f; // Increased range for longer beams
|
||||
|
||||
for (float i = 0; i < maxRange; i += 1f)
|
||||
{
|
||||
IntVec3 currentCell = (position.ToVector3() + direction * i).ToIntVec3();
|
||||
if (!currentCell.InBounds(pawn.Map))
|
||||
{
|
||||
return currentCell; // Reached map boundary
|
||||
}
|
||||
// Check for walls or other impassable terrain
|
||||
if (currentCell.GetEdifice(pawn.Map) != null && currentCell.GetEdifice(pawn.Map).def.passability == Traversability.Impassable)
|
||||
{
|
||||
return currentCell; // Hit an impassable wall
|
||||
}
|
||||
return (position.ToVector3() + direction * maxRange).ToIntVec3(); // Reached max range
|
||||
}
|
||||
|
||||
private bool CanUseCell(IntVec3 c)
|
||||
{
|
||||
return c.InBounds(this.CasterPawn.Map) && c != this.CasterPawn.Position;
|
||||
}
|
||||
|
||||
private List<IntVec3> tmpCells = new List<IntVec3>();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class VerbProperties_Wula_IonicBeam : VerbProperties
|
||||
{
|
||||
// --- Mode 1: Breaching Beam Properties ---
|
||||
public float breachingDamage = 200f;
|
||||
public float armorPenetration = 0.8f;
|
||||
public int breachingBeamDuration = 30; // Brief duration after hit calculation
|
||||
|
||||
// --- Mode 2: Sustained Beam Properties ---
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Verb_Wula_BreachingBeam : Verb
|
||||
{
|
||||
// --- Copied from Verb_ShootBeam for visual effects ---
|
||||
private MoteDualAttached mote;
|
||||
private Effecter endEffecter;
|
||||
private Sustainer sustainer;
|
||||
|
||||
// --- Our custom state ---
|
||||
private Vector3 beamEndPoint;
|
||||
private int ticksLeft;
|
||||
private bool beamHitMapEdge;
|
||||
private int explosionTicks;
|
||||
private float beamEnergy;
|
||||
|
||||
private VerbProperties_Wula_IonicBeam BeamProps => (VerbProperties_Wula_IonicBeam)verbProps;
|
||||
|
||||
public override float? AimAngleOverride => (state == VerbState.Bursting) ? (beamEndPoint - caster.DrawPos).AngleFlat() : (float?)null;
|
||||
|
||||
public override void WarmupComplete()
|
||||
{
|
||||
base.WarmupComplete();
|
||||
|
||||
// --- 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(), (int)verbProps.beamWidth);
|
||||
this.beamEnergy = BeamProps.breachingDamage;
|
||||
|
||||
// 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;
|
||||
var thingsToHit = cell.GetThingList(caster.Map).Where(t => CanHit(t)).ToList();
|
||||
|
||||
foreach (var thing in thingsToHit)
|
||||
{
|
||||
if (beamEnergy <= 0) break;
|
||||
|
||||
float damageToDeal = Mathf.Min(beamEnergy, thing.HitPoints);
|
||||
var dinfo = new DamageInfo(verbProps.beamDamageDef ?? DamageDefOf.Burn, damageToDeal, BeamProps.armorPenetration, shotAngle, caster, null, EquipmentSource?.def);
|
||||
|
||||
thing.TakeDamage(dinfo);
|
||||
beamEnergy -= thing.HitPoints;
|
||||
}
|
||||
|
||||
if (beamEnergy <= 0)
|
||||
{
|
||||
beamEndPoint = cell.ToVector3Shifted();
|
||||
beamHitMapEdge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Start Visual Effects ---
|
||||
if (verbProps.beamMoteDef != null)
|
||||
{
|
||||
mote = MoteMaker.MakeInteractionOverlay(verbProps.beamMoteDef, caster, new TargetInfo(beamEndPoint.ToIntVec3(), caster.Map));
|
||||
}
|
||||
if (verbProps.soundCastBeam != null)
|
||||
{
|
||||
sustainer = verbProps.soundCastBeam.TrySpawnSustainer(SoundInfo.InMap(caster, MaintenanceType.PerTick));
|
||||
}
|
||||
}
|
||||
|
||||
public override void BurstingTick()
|
||||
{
|
||||
if (ticksLeft > 0)
|
||||
{
|
||||
// --- Maintain Visual Effects ---
|
||||
if (mote != null)
|
||||
{
|
||||
mote.UpdateTargets(new TargetInfo(caster.Position, caster.Map), new TargetInfo(beamEndPoint.ToIntVec3(), caster.Map), Vector3.zero, Vector3.zero);
|
||||
mote.Maintain();
|
||||
}
|
||||
if (endEffecter == null && verbProps.beamEndEffecterDef != null)
|
||||
{
|
||||
endEffecter = verbProps.beamEndEffecterDef.Spawn(beamEndPoint.ToIntVec3(), caster.Map, Vector3.zero);
|
||||
}
|
||||
if (endEffecter != null)
|
||||
{
|
||||
endEffecter.EffectTick(new TargetInfo(beamEndPoint.ToIntVec3(), caster.Map), TargetInfo.Invalid);
|
||||
}
|
||||
sustainer?.Maintain();
|
||||
|
||||
// --- Path Explosion Logic ---
|
||||
if (BeamProps.explosionEnabled)
|
||||
{
|
||||
explosionTicks--;
|
||||
if (explosionTicks <= 0)
|
||||
{
|
||||
ApplyPathExplosionDamage();
|
||||
explosionTicks = BeamProps.explosionTickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
ticksLeft--;
|
||||
if (ticksLeft <= 0)
|
||||
{
|
||||
StopBeam();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryCastShot()
|
||||
{
|
||||
this.state = VerbState.Bursting;
|
||||
|
||||
if (beamHitMapEdge)
|
||||
{
|
||||
this.ticksLeft = BeamProps.breachingBeamDuration;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ticksLeft = 1;
|
||||
}
|
||||
|
||||
this.explosionTicks = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StopBeam()
|
||||
{
|
||||
this.state = VerbState.Idle;
|
||||
mote?.Destroy();
|
||||
endEffecter?.Cleanup();
|
||||
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)
|
||||
{
|
||||
return t != null && t.Spawned && t != caster && !t.def.IsFilth;
|
||||
}
|
||||
|
||||
private Vector3 GetMapEdgePoint(IntVec3 start, float angle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Verb_Wula_SustainedBeam : Verb
|
||||
{
|
||||
// --- Copied from Verb_ShootBeam for visual effects ---
|
||||
private MoteDualAttached mote;
|
||||
private Effecter endEffecter;
|
||||
private Sustainer sustainer;
|
||||
|
||||
// --- 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;
|
||||
|
||||
public override void WarmupComplete()
|
||||
{
|
||||
base.WarmupComplete();
|
||||
|
||||
var shotAngle = (currentTarget.Cell - caster.Position).AngleFlat;
|
||||
beamEnd = GetMapEdgePoint(caster.Position, shotAngle);
|
||||
|
||||
if (verbProps.beamMoteDef != null)
|
||||
{
|
||||
mote = MoteMaker.MakeInteractionOverlay(verbProps.beamMoteDef, caster, new TargetInfo(beamEnd.ToIntVec3(), caster.Map));
|
||||
}
|
||||
if (verbProps.soundCastBeam != null)
|
||||
{
|
||||
sustainer = verbProps.soundCastBeam.TrySpawnSustainer(SoundInfo.InMap(caster, MaintenanceType.PerTick));
|
||||
}
|
||||
}
|
||||
|
||||
public override void BurstingTick()
|
||||
{
|
||||
if (ticksLeft > 0)
|
||||
{
|
||||
// --- Maintain Visual Effects ---
|
||||
if (mote != null)
|
||||
{
|
||||
mote.UpdateTargets(new TargetInfo(caster.Position, caster.Map), new TargetInfo(beamEnd.ToIntVec3(), caster.Map), Vector3.zero, Vector3.zero);
|
||||
mote.Maintain();
|
||||
}
|
||||
if (endEffecter == null && verbProps.beamEndEffecterDef != null)
|
||||
{
|
||||
endEffecter = verbProps.beamEndEffecterDef.Spawn(beamEnd.ToIntVec3(), caster.Map, Vector3.zero);
|
||||
}
|
||||
if (endEffecter != null)
|
||||
{
|
||||
endEffecter.EffectTick(new TargetInfo(beamEnd.ToIntVec3(), caster.Map), TargetInfo.Invalid);
|
||||
}
|
||||
sustainer?.Maintain();
|
||||
|
||||
// --- Beam Damage Logic ---
|
||||
ticksToNextDamage--;
|
||||
if (ticksToNextDamage <= 0)
|
||||
{
|
||||
ApplyBeamDamage();
|
||||
ticksToNextDamage = BeamProps.tickInterval;
|
||||
}
|
||||
|
||||
// --- Path Explosion Logic ---
|
||||
if (BeamProps.explosionEnabled)
|
||||
{
|
||||
explosionTicks--;
|
||||
if (explosionTicks <= 0)
|
||||
{
|
||||
ApplyPathExplosionDamage();
|
||||
explosionTicks = BeamProps.explosionTickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
ticksLeft--;
|
||||
if (ticksLeft <= 0)
|
||||
{
|
||||
StopBeam();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryCastShot()
|
||||
{
|
||||
this.state = VerbState.Bursting;
|
||||
this.ticksLeft = BeamProps.duration;
|
||||
this.ticksToNextDamage = 0;
|
||||
this.explosionTicks = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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, null, EquipmentSource?.def);
|
||||
var cellsInBeam = WulaBeamUtility.GetCellsInBeamArea(caster.Position, beamEnd.ToIntVec3(), (int)verbProps.beamWidth);
|
||||
|
||||
foreach (var cell in cellsInBeam)
|
||||
{
|
||||
if (!cell.InBounds(caster.Map)) continue;
|
||||
|
||||
var thingsToHit = cell.GetThingList(caster.Map).Where(t => CanHit(t)).ToList();
|
||||
foreach (var thing in thingsToHit)
|
||||
{
|
||||
thing.TakeDamage(dinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyPathExplosionDamage()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
private bool CanHit(Thing t)
|
||||
{
|
||||
return t != null && t.Spawned && t != caster && !t.def.IsFilth;
|
||||
}
|
||||
|
||||
private Vector3 GetMapEdgePoint(IntVec3 start, float angle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public static class WulaBeamUtility
|
||||
{
|
||||
private static readonly Material BeamMaterial = MaterialPool.MatFrom(GenDraw.LineTexPath, ShaderDatabase.Transparent, Color.white);
|
||||
|
||||
// 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 beamLine.Distinct();
|
||||
}
|
||||
|
||||
var allCells = new HashSet<IntVec3>(beamLine);
|
||||
var halfWidth = (width - 1) / 2;
|
||||
|
||||
if (halfWidth == 0) return allCells;
|
||||
|
||||
var angle = (end - start).AngleFlat;
|
||||
var perpendicularAngle = angle - 90f;
|
||||
|
||||
foreach (var cell in beamLine)
|
||||
{
|
||||
for (int i = 1; i <= halfWidth; i++)
|
||||
{
|
||||
var offset = Vector3.forward.RotatedBy(perpendicularAngle) * i;
|
||||
allCells.Add((cell.ToVector3() + offset).ToIntVec3());
|
||||
allCells.Add((cell.ToVector3() - offset).ToIntVec3());
|
||||
}
|
||||
}
|
||||
return allCells;
|
||||
}
|
||||
|
||||
// A shared drawing method
|
||||
public static void DrawBeam(Vector3 start, Vector3 end, Color color, float width)
|
||||
{
|
||||
var material = BeamMaterial;
|
||||
if (material.color != color)
|
||||
{
|
||||
material = MaterialPool.MatFrom(GenDraw.LineTexPath, ShaderDatabase.Transparent, color);
|
||||
}
|
||||
|
||||
var matrix = default(Matrix4x4);
|
||||
var distance = Vector3.Distance(start, end);
|
||||
var angle = (end - start).AngleFlat();
|
||||
|
||||
matrix.SetTRS(
|
||||
pos: start + (end - start) / 2f,
|
||||
q: Quaternion.AngleAxis(angle, Vector3.up),
|
||||
s: new Vector3(width, 1f, distance)
|
||||
);
|
||||
|
||||
Graphics.DrawMesh(MeshPool.plane10, matrix, material, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,12 +191,12 @@
|
||||
<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" />
|
||||
<Compile Include="Verb\VerbProperties_Excalibur.cs" />
|
||||
<Compile Include="Verb\Verb_Excalibur.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Thing_ExcaliburBeam.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||
<Target Name="CleanDebugFiles" AfterTargets="Build">
|
||||
|
||||
Reference in New Issue
Block a user