光束炮
This commit is contained in:
Binary file not shown.
@@ -787,21 +787,22 @@
|
|||||||
</targetParams>
|
</targetParams>
|
||||||
</verbProperties>
|
</verbProperties>
|
||||||
<comps>
|
<comps>
|
||||||
<li Class="WulaFallenEmpire.CompProperties_GlobalFlyOverCooldown">
|
<!-- <li Class="WulaFallenEmpire.CompProperties_GlobalFlyOverCooldown">
|
||||||
<globalCooldownTicks>1000</globalCooldownTicks>
|
<globalCooldownTicks>1000</globalCooldownTicks>
|
||||||
<requiredFacility>BombardmentFacility</requiredFacility>
|
<requiredFacility>BombardmentFacility</requiredFacility>
|
||||||
</li>
|
</li> -->
|
||||||
<li Class="WulaFallenEmpire.CompProperties_EnergyLance">
|
<li Class="WulaFallenEmpire.CompProperties_AbilityEnergyLance">
|
||||||
<durationTicks>600</durationTicks>
|
<durationTicks>450</durationTicks>
|
||||||
<moveDistance>20</moveDistance>
|
<moveDistance>20</moveDistance>
|
||||||
<useFixedDistance>true</useFixedDistance>
|
<useFixedDistance>true</useFixedDistance>
|
||||||
<firesPerTick>3</firesPerTick>
|
<energyLanceDef>WULA_EnergyLance_Base</energyLanceDef>
|
||||||
|
<firesPerTick>4</firesPerTick>
|
||||||
</li>
|
</li>
|
||||||
<li Class="WulaFallenEmpire.CompProperties_RequireFlyOverFacility">
|
<!-- <li Class="WulaFallenEmpire.CompProperties_RequireFlyOverFacility"> -->
|
||||||
<!-- <flyOverDef></flyOverDef> -->
|
<!-- <flyOverDef></flyOverDef> -->
|
||||||
<requiredFacility>BombardmentFacility</requiredFacility>
|
<!-- <requiredFacility>BombardmentFacility</requiredFacility>
|
||||||
<facilityNotFoundMessage>需要拥有<color=#BD2F31><i>武器阵列</i></color>设施的战舰在地图上才能进行轨道炮击支援</facilityNotFoundMessage>
|
<facilityNotFoundMessage>需要拥有<color=#BD2F31><i>武器阵列</i></color>设施的战舰在地图上才能进行轨道炮击支援</facilityNotFoundMessage>
|
||||||
</li>
|
</li> -->
|
||||||
</comps>
|
</comps>
|
||||||
</AbilityDef>
|
</AbilityDef>
|
||||||
</Defs>
|
</Defs>
|
||||||
@@ -75,6 +75,8 @@
|
|||||||
<abilityDefs>
|
<abilityDefs>
|
||||||
<li>WULA_Firepower_Minigun_Strafe</li>
|
<li>WULA_Firepower_Minigun_Strafe</li>
|
||||||
<li>WULA_Firepower_Cannon_Salvo</li>
|
<li>WULA_Firepower_Cannon_Salvo</li>
|
||||||
|
<li>WULA_Firepower_Cannon_Surveillance_Beacon</li>
|
||||||
|
<li>WULA_Firepower_EnergyLance_Strafe</li>
|
||||||
</abilityDefs>
|
</abilityDefs>
|
||||||
</li>
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
|
|||||||
@@ -888,48 +888,117 @@
|
|||||||
</li>
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
<ThingDef ParentName="OrbitalStrikeBase">
|
<ThingDef ParentName="EtherealThingBase">
|
||||||
<defName>WULA_EnergyLance_Base</defName>
|
<defName>WULA_EnergyLance_Base</defName>
|
||||||
<label>power beam</label>
|
<label>乌拉帝国光矛</label>
|
||||||
<thingClass>PowerBeam</thingClass>
|
<description>移动的能量光束武器</description>
|
||||||
</ThingDef>
|
|
||||||
<ThingDef ParentName="OrbitalStrikeBase">
|
|
||||||
<defName>WULA_EnergyLance_Base</defName>
|
|
||||||
<label>energy lance</label>
|
|
||||||
<thingClass>WulaFallenEmpire.EnergyLance</thingClass>
|
<thingClass>WulaFallenEmpire.EnergyLance</thingClass>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<drawerType>RealtimeOnly</drawerType>
|
||||||
|
<drawOffscreen>true</drawOffscreen>
|
||||||
|
<seeThroughFog>true</seeThroughFog>
|
||||||
|
|
||||||
<modExtensions>
|
<modExtensions>
|
||||||
<li Class="WulaFallenEmpire.EnergyLanceExtension">
|
<li Class="WulaFallenEmpire.EnergyLanceExtension">
|
||||||
<damageDef>Flame</damageDef>
|
<moveSmoothing>0.001</moveSmoothing>
|
||||||
<igniteFires>true</igniteFires>
|
<moteSpawnInterval>3</moteSpawnInterval>
|
||||||
<fireIgniteChance>0.9</fireIgniteChance>
|
<moteScale>0.8</moteScale>
|
||||||
<damageAmountRange>
|
<firesPerTick>4</firesPerTick>
|
||||||
<min>60</min>
|
<effectRadius>15</effectRadius>
|
||||||
<max>95</max>
|
</li>
|
||||||
</damageAmountRange>
|
<li Class="WulaFallenEmpire.AbilityWeaponDefExtension">
|
||||||
<corpseDamageAmountRange>
|
<weaponDef>WULA_RW_Base_AR</weaponDef>
|
||||||
<min>5</min>
|
|
||||||
<max>10</max>
|
|
||||||
</corpseDamageAmountRange>
|
|
||||||
</li>
|
</li>
|
||||||
</modExtensions>
|
</modExtensions>
|
||||||
|
|
||||||
<comps>
|
<comps>
|
||||||
<!-- 使用原版的轨道光束组件 -->
|
<!-- 光束视觉效果 -->
|
||||||
<li Class="CompProperties_OrbitalBeam">
|
<li Class="CompProperties_OrbitalBeam">
|
||||||
<width>6</width>
|
<width>6</width>
|
||||||
<color>(255, 200, 50, 242)</color>
|
<color>(147, 116, 201, 212)</color>
|
||||||
<sound>OrbitalBeam</sound>
|
<sound>OrbitalBeam</sound>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!-- 相机震动 -->
|
||||||
<li Class="CompProperties_CameraShaker">
|
<li Class="CompProperties_CameraShaker">
|
||||||
<mag>0.02</mag>
|
<mag>0.02</mag>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!-- 天空效果 -->
|
||||||
<li Class="CompProperties_AffectsSky">
|
<li Class="CompProperties_AffectsSky">
|
||||||
|
<fadeInTicks>30</fadeInTicks>
|
||||||
|
<fadeOutTicks>15</fadeOutTicks>
|
||||||
<skyColors>
|
<skyColors>
|
||||||
<sky>(255, 220, 180)</sky>
|
<sky>(147, 116, 201)</sky>
|
||||||
<shadow>(220, 200, 160)</shadow>
|
<shadow>(147, 116, 185)</shadow>
|
||||||
<overlay>(255, 240, 200)</overlay>
|
<overlay>(147, 116, 150)</overlay>
|
||||||
<saturation>1.2</saturation>
|
<saturation>1.5</saturation>
|
||||||
</skyColors>
|
</skyColors>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li Class="CompProperties_Effecter">
|
||||||
|
<effecterDef>WULA_EnergyLance_Effecter</effecterDef>
|
||||||
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
|
<EffecterDef>
|
||||||
|
<defName>WULA_EnergyLance_Effecter</defName>
|
||||||
|
<maintainTicks>300</maintainTicks>
|
||||||
|
<children>
|
||||||
|
<li>
|
||||||
|
<subEffecterClass>SubEffecter_Sustainer</subEffecterClass>
|
||||||
|
<soundDef>VoidStructure_Emerging</soundDef>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<subEffecterClass>SubEffecter_SprayerChance</subEffecterClass>
|
||||||
|
<fleckDef>EnergyLance_Fleck</fleckDef>
|
||||||
|
<chancePerTick>1</chancePerTick>
|
||||||
|
<lifespanMaxTicks>999</lifespanMaxTicks>
|
||||||
|
<chancePeriodTicks>40</chancePeriodTicks>
|
||||||
|
<burstCount>1~1</burstCount>
|
||||||
|
<scale>15</scale>
|
||||||
|
<spawnLocType>OnSource</spawnLocType>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<subEffecterClass>SubEffecter_SprayerChance</subEffecterClass>
|
||||||
|
<fleckDef>EnergyLance_Fleck</fleckDef>
|
||||||
|
<chancePerTick>1</chancePerTick>
|
||||||
|
<lifespanMaxTicks>999</lifespanMaxTicks>
|
||||||
|
<chancePeriodTicks>40</chancePeriodTicks>
|
||||||
|
<burstCount>1~1</burstCount>
|
||||||
|
<scale>5</scale>
|
||||||
|
<spawnLocType>OnSource</spawnLocType>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<subEffecterClass>SubEffecter_SprayerChance</subEffecterClass>
|
||||||
|
<fleckDef>EnergyLance_Fleck</fleckDef>
|
||||||
|
<chancePerTick>1</chancePerTick>
|
||||||
|
<lifespanMaxTicks>999</lifespanMaxTicks>
|
||||||
|
<chancePeriodTicks>40</chancePeriodTicks>
|
||||||
|
<burstCount>1~1</burstCount>
|
||||||
|
<scale>2</scale>
|
||||||
|
<spawnLocType>OnSource</spawnLocType>
|
||||||
|
</li>
|
||||||
|
</children>
|
||||||
|
</EffecterDef>
|
||||||
|
<FleckDef ParentName="FleckBase">
|
||||||
|
<defName>EnergyLance_Fleck</defName>
|
||||||
|
<altitudeLayer>MoteOverhead</altitudeLayer>
|
||||||
|
<fadeInTime>1</fadeInTime>
|
||||||
|
<solidTime>0</solidTime>
|
||||||
|
<fadeOutTime>1</fadeOutTime>
|
||||||
|
<growthRate>5</growthRate>
|
||||||
|
<graphicData>
|
||||||
|
<graphicClass>Graphic_FleckPulse</graphicClass>
|
||||||
|
<texPath>Things/Mote/PowerBeam</texPath>
|
||||||
|
<shaderType>MoteGlow</shaderType>
|
||||||
|
<color>(147, 116, 201, 255)</color>
|
||||||
|
<shaderParameters>
|
||||||
|
<_NoiseTex>/Things/Mote/PsycastNoise</_NoiseTex>
|
||||||
|
<_NoiseIntensity>100</_NoiseIntensity>
|
||||||
|
<_Color2>(1, 1, 1, 0.85)</_Color2>
|
||||||
|
</shaderParameters>
|
||||||
|
<drawSize>10.0</drawSize>
|
||||||
|
</graphicData>
|
||||||
|
</FleckDef>
|
||||||
</Defs>
|
</Defs>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class AbilityWeaponDefExtension : DefModExtension
|
||||||
|
{
|
||||||
|
public ThingDef weaponDef; // 与此Ability相关的武器定义
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,124 +6,117 @@ namespace WulaFallenEmpire
|
|||||||
{
|
{
|
||||||
public class CompAbilityEffect_EnergyLance : CompAbilityEffect_WithDest
|
public class CompAbilityEffect_EnergyLance : CompAbilityEffect_WithDest
|
||||||
{
|
{
|
||||||
public new CompProperties_EnergyLance Props => (CompProperties_EnergyLance)props;
|
public new CompProperties_AbilityEnergyLance Props => (CompProperties_AbilityEnergyLance)props;
|
||||||
|
|
||||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||||
{
|
{
|
||||||
base.Apply(target, dest);
|
base.Apply(target, dest);
|
||||||
|
|
||||||
// 计算光束的起点和方向
|
if (parent.pawn == null || parent.pawn.Map == null)
|
||||||
IntVec3 startPos = target.Cell;
|
|
||||||
IntVec3 endPos = dest.Cell;
|
|
||||||
|
|
||||||
// 如果使用固定距离,则从起点向终点方向移动固定距离
|
|
||||||
if (Props.useFixedDistance)
|
|
||||||
{
|
|
||||||
Vector3 direction = (endPos.ToVector3() - startPos.ToVector3()).normalized;
|
|
||||||
Vector3 offset = direction * Props.moveDistance;
|
|
||||||
endPos = startPos + new IntVec3(Mathf.RoundToInt(offset.x), 0, Mathf.RoundToInt(offset.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建移动的能量光束
|
|
||||||
EnergyLance obj = (EnergyLance)GenSpawn.Spawn(ThingDef.Named("EnergyLance"), startPos, parent.pawn.Map);
|
|
||||||
obj.duration = Props.durationTicks;
|
|
||||||
obj.instigator = parent.pawn;
|
|
||||||
obj.startPos = startPos;
|
|
||||||
obj.endPos = endPos;
|
|
||||||
obj.moveDistance = Props.moveDistance;
|
|
||||||
obj.useFixedDistance = Props.useFixedDistance;
|
|
||||||
obj.firesPerTick = Props.firesPerTick;
|
|
||||||
// 不再需要传递伤害范围,因为现在从ModExtension读取
|
|
||||||
obj.StartStrike();
|
|
||||||
|
|
||||||
Log.Message($"[EnergyLance] Created energy lance from {startPos} to {endPos}, distance: {Props.moveDistance}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制预览效果
|
|
||||||
public override void DrawEffectPreview(LocalTargetInfo target)
|
|
||||||
{
|
|
||||||
base.DrawEffectPreview(target);
|
|
||||||
|
|
||||||
if (parent.pawn == null || parent.pawn.Map == null || !target.IsValid)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 绘制起点预览
|
// 使用配置的光束类型
|
||||||
GenDraw.DrawTargetHighlight(target.Cell);
|
ThingDef lanceDef = Props.energyLanceDef ?? ThingDef.Named("EnergyLance");
|
||||||
|
|
||||||
// 如果选择了终点,绘制移动路径预览
|
// 创建EnergyLance
|
||||||
if (selectedTarget.IsValid)
|
EnergyLance.MakeEnergyLance(
|
||||||
{
|
lanceDef,
|
||||||
DrawMovePathPreview(target.Cell, selectedTarget.Cell);
|
target.Cell,
|
||||||
}
|
dest.Cell,
|
||||||
|
parent.pawn.Map,
|
||||||
|
Props.moveDistance,
|
||||||
|
Props.useFixedDistance,
|
||||||
|
Props.durationTicks,
|
||||||
|
parent.pawn
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.Message($"[EnergyLance] Started {lanceDef.defName} from {target.Cell} to {dest.Cell}");
|
||||||
}
|
}
|
||||||
catch (System.Exception)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
// 忽略预览绘制错误
|
Log.Error($"[EnergyLance] Error starting EnergyLance: {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawMovePathPreview(IntVec3 startPos, IntVec3 endPos)
|
// 绘制预览保持不变
|
||||||
|
public new void DrawHighlight(LocalTargetInfo target)
|
||||||
|
{
|
||||||
|
if (selectedTarget.IsValid)
|
||||||
|
{
|
||||||
|
DrawBeamPathPreview(selectedTarget.Cell, target.Cell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GenDraw.DrawTargetHighlight(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawBeamPathPreview(IntVec3 startCell, IntVec3 endCell)
|
||||||
{
|
{
|
||||||
Map map = parent.pawn.Map;
|
Map map = parent.pawn.Map;
|
||||||
|
|
||||||
// 计算实际终点
|
Vector3 startPos = startCell.ToVector3();
|
||||||
IntVec3 actualEndPos = endPos;
|
Vector3 direction = (endCell.ToVector3() - startPos).normalized;
|
||||||
|
Vector3 actualEndPos;
|
||||||
|
|
||||||
if (Props.useFixedDistance)
|
if (Props.useFixedDistance)
|
||||||
{
|
{
|
||||||
Vector3 direction = (endPos.ToVector3() - startPos.ToVector3()).normalized;
|
actualEndPos = startPos + direction * Props.moveDistance;
|
||||||
Vector3 offset = direction * Props.moveDistance;
|
}
|
||||||
actualEndPos = startPos + new IntVec3(Mathf.RoundToInt(offset.x), 0, Mathf.RoundToInt(offset.z));
|
else
|
||||||
|
{
|
||||||
|
actualEndPos = endCell.ToVector3();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绘制移动路径
|
IntVec3 actualEndCell = new IntVec3(
|
||||||
Vector3 startVec = startPos.ToVector3Shifted();
|
Mathf.RoundToInt(actualEndPos.x),
|
||||||
Vector3 endVec = actualEndPos.ToVector3Shifted();
|
Mathf.RoundToInt(actualEndPos.y),
|
||||||
|
Mathf.RoundToInt(actualEndPos.z)
|
||||||
|
);
|
||||||
|
|
||||||
GenDraw.DrawLineBetween(startVec, endVec, SimpleColor.Yellow, 0.2f);
|
DrawBeamLine(startCell, actualEndCell);
|
||||||
|
GenDraw.DrawTargetHighlight(startCell);
|
||||||
// 绘制终点预览
|
GenDraw.DrawTargetHighlight(actualEndCell);
|
||||||
GenDraw.DrawTargetHighlight(actualEndPos);
|
DrawEffectRadiusPreview(startCell);
|
||||||
|
DrawEffectRadiusPreview(actualEndCell);
|
||||||
// 绘制作用范围预览(在移动路径上)
|
|
||||||
DrawEffectRangePreview(startPos, actualEndPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawEffectRangePreview(IntVec3 startPos, IntVec3 endPos)
|
private void DrawBeamLine(IntVec3 startCell, IntVec3 endCell)
|
||||||
|
{
|
||||||
|
Vector3 startPos = startCell.ToVector3Shifted();
|
||||||
|
Vector3 endPos = endCell.ToVector3Shifted();
|
||||||
|
|
||||||
|
GenDraw.DrawLineBetween(startPos, endPos, SimpleColor.Yellow, 0.3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawEffectRadiusPreview(IntVec3 center)
|
||||||
{
|
{
|
||||||
Map map = parent.pawn.Map;
|
Map map = parent.pawn.Map;
|
||||||
|
GenDraw.DrawRadiusRing(center, 15f, Color.yellow);
|
||||||
// 沿着移动路径绘制作用范围
|
|
||||||
Vector3 currentPos = startPos.ToVector3();
|
|
||||||
Vector3 direction = (endPos.ToVector3() - startPos.ToVector3()).normalized;
|
|
||||||
float totalDistance = Vector3.Distance(startPos.ToVector3(), endPos.ToVector3());
|
|
||||||
float step = 1f; // 每格绘制
|
|
||||||
|
|
||||||
for (float distance = 0; distance <= totalDistance; distance += step)
|
|
||||||
{
|
|
||||||
Vector3 checkPos = startPos.ToVector3() + direction * distance;
|
|
||||||
IntVec3 checkCell = new IntVec3(Mathf.RoundToInt(checkPos.x), 0, Mathf.RoundToInt(checkPos.z));
|
|
||||||
|
|
||||||
if (checkCell.InBounds(map))
|
|
||||||
{
|
|
||||||
// 绘制作用范围指示
|
|
||||||
GenDraw.DrawRadiusRing(checkCell, 1.5f, Color.red, (c) => true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
|
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
|
||||||
{
|
{
|
||||||
string baseInfo = $"能量长矛: 持续{Props.durationTicks}刻";
|
if (selectedTarget.IsValid)
|
||||||
|
|
||||||
if (Props.useFixedDistance)
|
|
||||||
{
|
{
|
||||||
baseInfo += $"\n移动距离: {Props.moveDistance}格";
|
string beamType = Props.energyLanceDef?.label ?? "EnergyLance";
|
||||||
|
return $"选择{beamType}方向\n移动距离: {Props.moveDistance}格\n模式: {(Props.useFixedDistance ? "固定距离" : "移动到终点")}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "选择光束起点";
|
||||||
}
|
}
|
||||||
|
|
||||||
baseInfo += $"\n选择起点后,再选择移动方向";
|
|
||||||
|
|
||||||
return baseInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override TargetingParameters targetParams => new TargetingParameters
|
||||||
|
{
|
||||||
|
canTargetLocations = true,
|
||||||
|
canTargetPawns = false,
|
||||||
|
canTargetBuildings = false,
|
||||||
|
canTargetItems = false,
|
||||||
|
mapObjectTargetsMustBeAutoAttackable = false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class CompProperties_AbilityEnergyLance : CompProperties_EffectWithDest
|
||||||
|
{
|
||||||
|
// 光束持续时间
|
||||||
|
public int durationTicks = 600;
|
||||||
|
|
||||||
|
// 移动配置
|
||||||
|
public float moveDistance = 15f;
|
||||||
|
public bool useFixedDistance = true;
|
||||||
|
|
||||||
|
// 光束类型配置 - 新增:暴露光束类型
|
||||||
|
public ThingDef energyLanceDef; // 使用的EnergyLance ThingDef
|
||||||
|
public int firesPerTick = 4; // 每刻造成的火灾数量
|
||||||
|
|
||||||
|
public CompProperties_AbilityEnergyLance()
|
||||||
|
{
|
||||||
|
this.compClass = typeof(CompAbilityEffect_EnergyLance);
|
||||||
|
this.destination = AbilityEffectDestination.Selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using RimWorld;
|
|
||||||
using Verse;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class CompProperties_EnergyLance : CompProperties_EffectWithDest
|
|
||||||
{
|
|
||||||
public int durationTicks = 600; // 光束持续时间
|
|
||||||
public float moveDistance = 15f; // 光束移动距离
|
|
||||||
public bool useFixedDistance = true; // 是否使用固定距离
|
|
||||||
|
|
||||||
// 伤害配置
|
|
||||||
public int firesPerTick = 4; // 每刻产生的火焰数量
|
|
||||||
public IntRange flameDamageRange = new IntRange(65, 100); // 火焰伤害范围
|
|
||||||
public IntRange corpseFlameDamageRange = new IntRange(5, 10); // 尸体火焰伤害范围
|
|
||||||
|
|
||||||
public CompProperties_EnergyLance()
|
|
||||||
{
|
|
||||||
this.compClass = typeof(CompAbilityEffect_EnergyLance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,82 +3,141 @@ using Verse;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Verse.Sound;
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
namespace WulaFallenEmpire
|
||||||
{
|
{
|
||||||
public class EnergyLance : OrbitalStrike
|
[StaticConstructorOnStartup]
|
||||||
|
public class EnergyLance : ThingWithComps
|
||||||
{
|
{
|
||||||
// 移动相关属性
|
// 移动相关属性
|
||||||
public IntVec3 startPos;
|
public IntVec3 startPosition;
|
||||||
public IntVec3 endPos;
|
public IntVec3 endPosition;
|
||||||
public float moveDistance;
|
public float moveDistance;
|
||||||
public bool useFixedDistance;
|
public bool useFixedDistance;
|
||||||
|
public float flightSpeed = 1f;
|
||||||
|
public float currentProgress = 0f;
|
||||||
|
public float altitude = 20f;
|
||||||
|
|
||||||
// 伤害配置
|
// 伤害配置
|
||||||
public int firesPerTick = 4;
|
public int firesPerTick = 4;
|
||||||
|
public float effectRadius = 15f;
|
||||||
// ModExtension引用
|
public int durationTicks = 600;
|
||||||
private EnergyLanceExtension extension;
|
private int ticksPassed = 0;
|
||||||
|
|
||||||
// 移动状态
|
// 移动状态
|
||||||
private Vector3 currentPos;
|
private Vector3 exactPosition;
|
||||||
private Vector3 moveDirection;
|
private Vector3 moveDirection;
|
||||||
private float moveSpeed;
|
private bool hasStarted = false;
|
||||||
private float traveledDistance;
|
private bool hasCompleted = false;
|
||||||
private const float effectRadius = 3f; // 作用半径
|
|
||||||
|
|
||||||
|
// 视觉效果
|
||||||
|
private CompOrbitalBeam orbitalBeamComp;
|
||||||
|
private Sustainer sustainer;
|
||||||
|
|
||||||
|
// 伤害相关
|
||||||
private static List<Thing> tmpThings = new List<Thing>();
|
private static List<Thing> tmpThings = new List<Thing>();
|
||||||
|
private static readonly IntRange FlameDamageAmountRange = new IntRange(65, 100);
|
||||||
|
private static readonly IntRange CorpseFlameDamageAmountRange = new IntRange(5, 10);
|
||||||
|
public Thing instigator;
|
||||||
|
public ThingDef weaponDef;
|
||||||
|
|
||||||
public override void StartStrike()
|
// 精确位置计算(基于FlyOver的逻辑)
|
||||||
|
public override Vector3 DrawPos
|
||||||
{
|
{
|
||||||
base.StartStrike();
|
get
|
||||||
|
|
||||||
// 获取ModExtension
|
|
||||||
extension = def.GetModExtension<EnergyLanceExtension>();
|
|
||||||
if (extension == null)
|
|
||||||
{
|
{
|
||||||
Log.Error($"[EnergyLance] No EnergyLanceExtension found on {def.defName}");
|
Vector3 start = startPosition.ToVector3();
|
||||||
return;
|
Vector3 end = CalculateEndPosition();
|
||||||
|
Vector3 basePos = Vector3.Lerp(start, end, currentProgress);
|
||||||
|
basePos.y = altitude;
|
||||||
|
return basePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化移动参数
|
|
||||||
currentPos = startPos.ToVector3();
|
|
||||||
|
|
||||||
if (useFixedDistance)
|
|
||||||
{
|
|
||||||
// 从起点向终点方向移动固定距离
|
|
||||||
Vector3 direction = (endPos.ToVector3() - startPos.ToVector3()).normalized;
|
|
||||||
moveDirection = direction;
|
|
||||||
moveSpeed = moveDistance / duration; // 根据持续时间计算移动速度
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 直接从起点移动到终点
|
|
||||||
Vector3 direction = (endPos.ToVector3() - startPos.ToVector3());
|
|
||||||
moveDirection = direction.normalized;
|
|
||||||
moveSpeed = direction.magnitude / duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
traveledDistance = 0f;
|
|
||||||
|
|
||||||
// 创建视觉效果
|
|
||||||
CreateVisualEffect();
|
|
||||||
|
|
||||||
Log.Message($"[EnergyLance] Strike started from {startPos} to {endPos}, " +
|
|
||||||
$"damage: {extension.damageDef.defName}, speed: {moveSpeed}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateVisualEffect()
|
// 计算实际终点位置
|
||||||
|
private Vector3 CalculateEndPosition()
|
||||||
{
|
{
|
||||||
// 使用ModExtension中定义的Mote,如果没有则使用默认的PowerBeam
|
if (useFixedDistance)
|
||||||
if (extension.moteDef != null)
|
|
||||||
{
|
{
|
||||||
Mote mote = MoteMaker.MakeStaticMote(base.Position, base.Map, extension.moteDef);
|
Vector3 direction = (endPosition.ToVector3() - startPosition.ToVector3()).normalized;
|
||||||
|
return startPosition.ToVector3() + direction * moveDistance;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 使用原版PowerBeam的视觉效果
|
return endPosition.ToVector3();
|
||||||
MoteMaker.MakePowerBeamMote(base.Position, base.Map);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 精确旋转
|
||||||
|
public virtual Quaternion ExactRotation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Vector3 direction = (CalculateEndPosition() - startPosition.ToVector3()).normalized;
|
||||||
|
return Quaternion.LookRotation(direction.Yto0());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.SpawnSetup(map, respawningAfterLoad);
|
||||||
|
|
||||||
|
orbitalBeamComp = GetComp<CompOrbitalBeam>();
|
||||||
|
|
||||||
|
if (!respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.Position = startPosition;
|
||||||
|
hasStarted = true;
|
||||||
|
|
||||||
|
// 计算移动方向
|
||||||
|
Vector3 endPos = CalculateEndPosition();
|
||||||
|
moveDirection = (endPos - startPosition.ToVector3()).normalized;
|
||||||
|
|
||||||
|
// 初始化光束组件
|
||||||
|
if (orbitalBeamComp != null)
|
||||||
|
{
|
||||||
|
// 使用反射调用StartAnimation方法
|
||||||
|
StartOrbitalBeamAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始音效
|
||||||
|
StartSound();
|
||||||
|
|
||||||
|
Log.Message($"[EnergyLance] Spawned at {startPosition}, moving to {endPosition}, " +
|
||||||
|
$"distance: {moveDistance}, fixed: {useFixedDistance}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用反射调用StartAnimation方法
|
||||||
|
private void StartOrbitalBeamAnimation()
|
||||||
|
{
|
||||||
|
var startAnimationMethod = orbitalBeamComp.GetType().GetMethod("StartAnimation");
|
||||||
|
if (startAnimationMethod != null)
|
||||||
|
{
|
||||||
|
startAnimationMethod.Invoke(orbitalBeamComp, new object[] { durationTicks, 10, 0f });
|
||||||
|
Log.Message("[EnergyLance] Orbital beam animation started");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Warning("[EnergyLance] Could not find StartAnimation method on CompOrbitalBeam");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartSound()
|
||||||
|
{
|
||||||
|
var soundProp = orbitalBeamComp?.GetType().GetProperty("Props")?.GetValue(orbitalBeamComp);
|
||||||
|
if (soundProp != null)
|
||||||
|
{
|
||||||
|
var soundField = soundProp.GetType().GetField("sound");
|
||||||
|
if (soundField != null)
|
||||||
|
{
|
||||||
|
SoundDef soundDef = soundField.GetValue(soundProp) as SoundDef;
|
||||||
|
if (soundDef != null)
|
||||||
|
{
|
||||||
|
sustainer = soundDef.TrySpawnSustainer(SoundInfo.InMap(this, MaintenanceType.PerTick));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,110 +145,142 @@ namespace WulaFallenEmpire
|
|||||||
{
|
{
|
||||||
base.Tick();
|
base.Tick();
|
||||||
|
|
||||||
if (!base.Destroyed && extension != null)
|
if (!hasStarted || hasCompleted)
|
||||||
{
|
return;
|
||||||
// 移动光束
|
|
||||||
MoveBeam();
|
|
||||||
|
|
||||||
// 造成伤害
|
ticksPassed++;
|
||||||
for (int i = 0; i < firesPerTick; i++)
|
|
||||||
{
|
// 更新移动进度
|
||||||
DoBeamDamage();
|
UpdateMovement();
|
||||||
}
|
|
||||||
|
// 造成伤害
|
||||||
|
for (int i = 0; i < firesPerTick; i++)
|
||||||
|
{
|
||||||
|
StartRandomFireAndDoFlameDamage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新音效
|
||||||
|
sustainer?.Maintain();
|
||||||
|
|
||||||
|
// 检查是否完成
|
||||||
|
if (ticksPassed >= durationTicks || currentProgress >= 1f)
|
||||||
|
{
|
||||||
|
CompleteEnergyLance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoveBeam()
|
private void UpdateMovement()
|
||||||
{
|
{
|
||||||
// 计算移动距离
|
// 计算总距离
|
||||||
float moveThisTick = moveSpeed;
|
float totalDistance = useFixedDistance ? moveDistance : Vector3.Distance(startPosition.ToVector3(), endPosition.ToVector3());
|
||||||
|
|
||||||
// 更新位置
|
// 计算移动速度(基于持续时间和总距离)
|
||||||
currentPos += moveDirection * moveThisTick;
|
float progressPerTick = 1f / durationTicks;
|
||||||
traveledDistance += moveThisTick;
|
currentProgress += progressPerTick;
|
||||||
|
currentProgress = Mathf.Clamp01(currentProgress);
|
||||||
|
|
||||||
|
// 更新精确位置
|
||||||
|
exactPosition = Vector3.Lerp(startPosition.ToVector3(), CalculateEndPosition(), currentProgress);
|
||||||
|
|
||||||
|
// 更新格子位置
|
||||||
|
IntVec3 newCell = new IntVec3(
|
||||||
|
Mathf.RoundToInt(exactPosition.x),
|
||||||
|
Mathf.RoundToInt(exactPosition.y),
|
||||||
|
Mathf.RoundToInt(exactPosition.z)
|
||||||
|
);
|
||||||
|
|
||||||
// 更新光束的实际位置
|
|
||||||
IntVec3 newCell = new IntVec3(Mathf.RoundToInt(currentPos.x), 0, Mathf.RoundToInt(currentPos.z));
|
|
||||||
if (newCell != base.Position && newCell.InBounds(base.Map))
|
if (newCell != base.Position && newCell.InBounds(base.Map))
|
||||||
{
|
{
|
||||||
base.Position = newCell;
|
base.Position = newCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否到达终点
|
|
||||||
if (useFixedDistance && traveledDistance >= moveDistance)
|
|
||||||
{
|
|
||||||
// 固定距离模式:移动指定距离后结束
|
|
||||||
Destroy();
|
|
||||||
Log.Message($"[EnergyLance] Reached fixed distance, destroying");
|
|
||||||
}
|
|
||||||
else if (!useFixedDistance && traveledDistance >= Vector3.Distance(startPos.ToVector3(), endPos.ToVector3()))
|
|
||||||
{
|
|
||||||
// 终点模式:到达终点后结束
|
|
||||||
Destroy();
|
|
||||||
Log.Message($"[EnergyLance] Reached end position, destroying");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoBeamDamage()
|
private void StartRandomFireAndDoFlameDamage()
|
||||||
{
|
{
|
||||||
if (extension == null) return;
|
|
||||||
|
|
||||||
// 在当前光束位置周围随机选择一个单元格
|
|
||||||
IntVec3 targetCell = (from x in GenRadial.RadialCellsAround(base.Position, effectRadius, useCenter: true)
|
IntVec3 targetCell = (from x in GenRadial.RadialCellsAround(base.Position, effectRadius, useCenter: true)
|
||||||
where x.InBounds(base.Map)
|
where x.InBounds(base.Map)
|
||||||
select x).RandomElementByWeight((IntVec3 x) => 1f - Mathf.Min(x.DistanceTo(base.Position) / effectRadius, 1f) + 0.05f);
|
select x).RandomElementByWeight((IntVec3 x) => 1f - Mathf.Min(x.DistanceTo(base.Position) / effectRadius, 1f) + 0.05f);
|
||||||
|
|
||||||
// 尝试在该单元格点火(如果配置了点火)
|
FireUtility.TryStartFireIn(targetCell, base.Map, Rand.Range(0.1f, 0.925f), instigator);
|
||||||
if (extension.igniteFires)
|
|
||||||
{
|
|
||||||
FireUtility.TryStartFireIn(targetCell, base.Map, Rand.Range(0.1f, extension.fireIgniteChance), instigator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对该单元格内的物体造成伤害
|
|
||||||
tmpThings.Clear();
|
tmpThings.Clear();
|
||||||
tmpThings.AddRange(targetCell.GetThingList(base.Map));
|
tmpThings.AddRange(targetCell.GetThingList(base.Map));
|
||||||
|
|
||||||
for (int i = 0; i < tmpThings.Count; i++)
|
for (int i = 0; i < tmpThings.Count; i++)
|
||||||
{
|
{
|
||||||
Thing thing = tmpThings[i];
|
int num = ((tmpThings[i] is Corpse) ? CorpseFlameDamageAmountRange.RandomInRange : FlameDamageAmountRange.RandomInRange);
|
||||||
|
Pawn pawn = tmpThings[i] as Pawn;
|
||||||
// 检查是否对尸体造成伤害
|
BattleLogEntry_DamageTaken battleLogEntry_DamageTaken = null;
|
||||||
if (!extension.applyDamageToCorpses && thing is Corpse)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 计算伤害量
|
|
||||||
int damageAmount = (thing is Corpse) ?
|
|
||||||
extension.corpseDamageAmountRange.RandomInRange :
|
|
||||||
extension.damageAmountRange.RandomInRange;
|
|
||||||
|
|
||||||
Pawn pawn = thing as Pawn;
|
|
||||||
BattleLogEntry_DamageTaken battleLogEntry = null;
|
|
||||||
|
|
||||||
if (pawn != null)
|
if (pawn != null)
|
||||||
{
|
{
|
||||||
battleLogEntry = new BattleLogEntry_DamageTaken(pawn, RulePackDefOf.DamageEvent_PowerBeam, instigator as Pawn);
|
battleLogEntry_DamageTaken = new BattleLogEntry_DamageTaken(pawn, RulePackDefOf.DamageEvent_PowerBeam, instigator as Pawn);
|
||||||
Find.BattleLog.Add(battleLogEntry);
|
Find.BattleLog.Add(battleLogEntry_DamageTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用ModExtension中定义的伤害类型
|
DamageInfo damageInfo = new DamageInfo(DamageDefOf.Flame, num, 0f, -1f, instigator, null, weaponDef);
|
||||||
DamageInfo damageInfo = new DamageInfo(extension.damageDef, damageAmount, 0f, -1f, instigator, null, weaponDef);
|
tmpThings[i].TakeDamage(damageInfo).AssociateWithLog(battleLogEntry_DamageTaken);
|
||||||
thing.TakeDamage(damageInfo).AssociateWithLog(battleLogEntry);
|
|
||||||
|
|
||||||
Log.Message($"[EnergyLance] Applied {extension.damageDef.defName} damage {damageAmount} to {thing.Label}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpThings.Clear();
|
tmpThings.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CompleteEnergyLance()
|
||||||
|
{
|
||||||
|
hasCompleted = true;
|
||||||
|
|
||||||
|
// 停止音效
|
||||||
|
sustainer?.End();
|
||||||
|
sustainer = null;
|
||||||
|
|
||||||
|
Log.Message($"[EnergyLance] Completed at position {base.Position}");
|
||||||
|
|
||||||
|
// 销毁自身
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重写绘制方法,确保光束正确显示
|
||||||
|
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||||
|
{
|
||||||
|
// 让CompOrbitalBeam处理绘制
|
||||||
|
Comps_PostDraw();
|
||||||
|
}
|
||||||
|
|
||||||
public override void ExposeData()
|
public override void ExposeData()
|
||||||
{
|
{
|
||||||
base.ExposeData();
|
base.ExposeData();
|
||||||
|
|
||||||
Scribe_Values.Look(ref startPos, "startPos");
|
Scribe_Values.Look(ref startPosition, "startPosition");
|
||||||
Scribe_Values.Look(ref endPos, "endPos");
|
Scribe_Values.Look(ref endPosition, "endPosition");
|
||||||
Scribe_Values.Look(ref moveDistance, "moveDistance");
|
Scribe_Values.Look(ref moveDistance, "moveDistance");
|
||||||
Scribe_Values.Look(ref useFixedDistance, "useFixedDistance");
|
Scribe_Values.Look(ref useFixedDistance, "useFixedDistance");
|
||||||
|
Scribe_Values.Look(ref flightSpeed, "flightSpeed", 1f);
|
||||||
|
Scribe_Values.Look(ref currentProgress, "currentProgress", 0f);
|
||||||
|
Scribe_Values.Look(ref altitude, "altitude", 20f);
|
||||||
Scribe_Values.Look(ref firesPerTick, "firesPerTick", 4);
|
Scribe_Values.Look(ref firesPerTick, "firesPerTick", 4);
|
||||||
|
Scribe_Values.Look(ref effectRadius, "effectRadius", 15f);
|
||||||
|
Scribe_Values.Look(ref durationTicks, "durationTicks", 600);
|
||||||
|
Scribe_Values.Look(ref ticksPassed, "ticksPassed", 0);
|
||||||
|
Scribe_Values.Look(ref hasStarted, "hasStarted", false);
|
||||||
|
Scribe_Values.Look(ref hasCompleted, "hasCompleted", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建EnergyLance的静态方法
|
||||||
|
public static EnergyLance MakeEnergyLance(ThingDef energyLanceDef, IntVec3 start, IntVec3 end, Map map,
|
||||||
|
float distance = 15f, bool fixedDistance = true, int duration = 600, Pawn instigatorPawn = null)
|
||||||
|
{
|
||||||
|
EnergyLance energyLance = (EnergyLance)ThingMaker.MakeThing(energyLanceDef);
|
||||||
|
energyLance.startPosition = start;
|
||||||
|
energyLance.endPosition = end;
|
||||||
|
energyLance.moveDistance = distance;
|
||||||
|
energyLance.useFixedDistance = fixedDistance;
|
||||||
|
energyLance.durationTicks = duration;
|
||||||
|
energyLance.instigator = instigatorPawn;
|
||||||
|
|
||||||
|
GenSpawn.Spawn(energyLance, start, map);
|
||||||
|
|
||||||
|
Log.Message($"[EnergyLance] Created {energyLanceDef.defName} from {start} to {end}");
|
||||||
|
return energyLance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,13 @@ namespace WulaFallenEmpire
|
|||||||
{
|
{
|
||||||
public class EnergyLanceExtension : DefModExtension
|
public class EnergyLanceExtension : DefModExtension
|
||||||
{
|
{
|
||||||
// 伤害类型配置
|
// 移动平滑配置
|
||||||
public DamageDef damageDef = DamageDefOf.Flame; // 伤害类型,默认为火焰伤害
|
public float moveSmoothing = 0.1f; // 移动平滑度 (0-1),值越小越平滑
|
||||||
public bool applyDamageToCorpses = true; // 是否对尸体造成伤害
|
public int moteSpawnInterval = 3; // Mote生成间隔,值越大密度越低
|
||||||
public bool igniteFires = true; // 是否点燃火焰
|
public float moteScale = 0.8f; // Mote缩放比例
|
||||||
public float fireIgniteChance = 0.8f; // 点燃火焰的概率
|
|
||||||
|
|
||||||
// 伤害量配置
|
// 伤害配置
|
||||||
public IntRange damageAmountRange = new IntRange(65, 100); // 对生物的伤害范围
|
public int firesPerTick = 4;
|
||||||
public IntRange corpseDamageAmountRange = new IntRange(5, 10); // 对尸体的伤害范围
|
public float effectRadius = 15f;
|
||||||
|
|
||||||
// 视觉效果配置
|
|
||||||
public ThingDef moteDef; // 使用的Mote类型
|
|
||||||
public SoundDef impactSound; // 撞击音效
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,361 +0,0 @@
|
|||||||
using RimWorld;
|
|
||||||
using Verse;
|
|
||||||
using UnityEngine;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class CompBuildingEnergyLance : ThingComp
|
|
||||||
{
|
|
||||||
public CompProperties_BuildingEnergyLance Props => (CompProperties_BuildingEnergyLance)props;
|
|
||||||
|
|
||||||
// 状态管理
|
|
||||||
private EnergyLanceState currentState = EnergyLanceState.Idle;
|
|
||||||
private int nextUpdateTick = 0;
|
|
||||||
private int noTargetTicks = 0;
|
|
||||||
|
|
||||||
// 目标管理
|
|
||||||
private Pawn currentTarget = null;
|
|
||||||
private IntVec3 lastTargetPosition = IntVec3.Invalid;
|
|
||||||
|
|
||||||
// EnergyLance实例
|
|
||||||
private GuidedEnergyLance activeLance = null;
|
|
||||||
|
|
||||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
|
||||||
{
|
|
||||||
base.PostSpawnSetup(respawningAfterLoad);
|
|
||||||
|
|
||||||
if (!respawningAfterLoad)
|
|
||||||
{
|
|
||||||
// 新生成时立即开始搜索目标
|
|
||||||
nextUpdateTick = Find.TickManager.TicksGame;
|
|
||||||
currentState = EnergyLanceState.Searching;
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] Building spawned, starting target search");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateState()
|
|
||||||
{
|
|
||||||
switch (currentState)
|
|
||||||
{
|
|
||||||
case EnergyLanceState.Idle:
|
|
||||||
// 空闲状态,等待下一次更新
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EnergyLanceState.Searching:
|
|
||||||
SearchForTarget();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EnergyLanceState.Tracking:
|
|
||||||
TrackTarget();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EnergyLanceState.NoTarget:
|
|
||||||
HandleNoTarget();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SearchForTarget()
|
|
||||||
{
|
|
||||||
Map map = parent.Map;
|
|
||||||
if (map == null) return;
|
|
||||||
|
|
||||||
// 获取范围内的所有有效目标
|
|
||||||
var potentialTargets = new List<Pawn>();
|
|
||||||
|
|
||||||
foreach (var pawn in map.mapPawns.AllPawnsSpawned)
|
|
||||||
{
|
|
||||||
if (IsValidTarget(pawn) && IsInRange(pawn.Position))
|
|
||||||
{
|
|
||||||
potentialTargets.Add(pawn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (potentialTargets.Count > 0)
|
|
||||||
{
|
|
||||||
// 选择第一个目标(可以改为其他选择逻辑)
|
|
||||||
currentTarget = potentialTargets[0];
|
|
||||||
lastTargetPosition = currentTarget.Position;
|
|
||||||
|
|
||||||
// 创建EnergyLance
|
|
||||||
CreateEnergyLance();
|
|
||||||
|
|
||||||
currentState = EnergyLanceState.Tracking;
|
|
||||||
noTargetTicks = 0;
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] Locked target: {currentTarget.Label}, position: {currentTarget.Position}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 没有找到目标
|
|
||||||
currentState = EnergyLanceState.NoTarget;
|
|
||||||
Log.Message($"[BuildingEnergyLance] No targets found in range");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TrackTarget()
|
|
||||||
{
|
|
||||||
if (currentTarget == null || !currentTarget.Spawned)
|
|
||||||
{
|
|
||||||
// 目标丢失,重新搜索
|
|
||||||
currentState = EnergyLanceState.Searching;
|
|
||||||
currentTarget = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查目标是否仍然有效
|
|
||||||
if (!IsValidTarget(currentTarget) || !IsInRange(currentTarget.Position))
|
|
||||||
{
|
|
||||||
// 目标无效或超出范围,寻找最近的敌人
|
|
||||||
FindNearestTarget();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新目标位置
|
|
||||||
lastTargetPosition = currentTarget.Position;
|
|
||||||
|
|
||||||
// 向EnergyLance发送目标位置
|
|
||||||
if (activeLance != null && !activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance.UpdateTarget(lastTargetPosition);
|
|
||||||
noTargetTicks = 0;
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] Updated target position: {lastTargetPosition}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// EnergyLance丢失,重新创建
|
|
||||||
CreateEnergyLance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FindNearestTarget()
|
|
||||||
{
|
|
||||||
Map map = parent.Map;
|
|
||||||
if (map == null) return;
|
|
||||||
|
|
||||||
Pawn nearestTarget = null;
|
|
||||||
float nearestDistance = float.MaxValue;
|
|
||||||
|
|
||||||
// 获取当前EnergyLance位置
|
|
||||||
IntVec3 searchCenter = (activeLance != null && !activeLance.Destroyed) ?
|
|
||||||
activeLance.Position : parent.Position;
|
|
||||||
|
|
||||||
foreach (var pawn in map.mapPawns.AllPawnsSpawned)
|
|
||||||
{
|
|
||||||
if (IsValidTarget(pawn) && IsInRange(pawn.Position))
|
|
||||||
{
|
|
||||||
float distance = Vector3.Distance(searchCenter.ToVector3(), pawn.Position.ToVector3());
|
|
||||||
if (distance < nearestDistance)
|
|
||||||
{
|
|
||||||
nearestDistance = distance;
|
|
||||||
nearestTarget = pawn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nearestTarget != null)
|
|
||||||
{
|
|
||||||
currentTarget = nearestTarget;
|
|
||||||
lastTargetPosition = currentTarget.Position;
|
|
||||||
|
|
||||||
// 更新EnergyLance目标
|
|
||||||
if (activeLance != null && !activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance.UpdateTarget(lastTargetPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] Switched to nearest target: {currentTarget.Label}, distance: {nearestDistance}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 没有找到替代目标
|
|
||||||
currentState = EnergyLanceState.NoTarget;
|
|
||||||
currentTarget = null;
|
|
||||||
Log.Message($"[BuildingEnergyLance] No alternative targets found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleNoTarget()
|
|
||||||
{
|
|
||||||
noTargetTicks++;
|
|
||||||
|
|
||||||
// 向EnergyLance发送空位置
|
|
||||||
if (activeLance != null && !activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance.UpdateTarget(IntVec3.Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否应该销毁EnergyLance
|
|
||||||
if (noTargetTicks >= Props.maxNoTargetTicks)
|
|
||||||
{
|
|
||||||
if (activeLance != null && !activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance.Destroy();
|
|
||||||
activeLance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回到搜索状态
|
|
||||||
currentState = EnergyLanceState.Searching;
|
|
||||||
noTargetTicks = 0;
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] EnergyLance destroyed due to no targets");
|
|
||||||
}
|
|
||||||
else if (noTargetTicks % 60 == 0) // 每60刻检查一次是否有新目标
|
|
||||||
{
|
|
||||||
SearchForTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateEnergyLance()
|
|
||||||
{
|
|
||||||
if (Props.energyLanceDef == null)
|
|
||||||
{
|
|
||||||
Log.Error($"[BuildingEnergyLance] No energyLanceDef configured");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 销毁现有的EnergyLance
|
|
||||||
if (activeLance != null && !activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建新的EnergyLance
|
|
||||||
IntVec3 spawnPos = GetLanceSpawnPosition();
|
|
||||||
activeLance = (GuidedEnergyLance)GenSpawn.Spawn(Props.energyLanceDef, spawnPos, parent.Map);
|
|
||||||
|
|
||||||
// 初始化EnergyLance
|
|
||||||
activeLance.duration = Props.energyLanceDuration;
|
|
||||||
activeLance.instigator = parent;
|
|
||||||
activeLance.controlBuilding = this.parent;
|
|
||||||
activeLance.firesPerTick = Props.firesPerTick;
|
|
||||||
|
|
||||||
// 如果有当前目标,设置初始位置
|
|
||||||
if (currentTarget != null)
|
|
||||||
{
|
|
||||||
activeLance.UpdateTarget(currentTarget.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
activeLance.StartStrike();
|
|
||||||
|
|
||||||
Log.Message($"[BuildingEnergyLance] Created EnergyLance at {spawnPos}");
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error($"[BuildingEnergyLance] Error creating EnergyLance: {ex}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntVec3 GetLanceSpawnPosition()
|
|
||||||
{
|
|
||||||
// 在建筑周围寻找一个合适的生成位置
|
|
||||||
Map map = parent.Map;
|
|
||||||
IntVec3 center = parent.Position;
|
|
||||||
|
|
||||||
// 优先选择建筑上方的位置
|
|
||||||
if (center.InBounds(map) && center.Walkable(map))
|
|
||||||
{
|
|
||||||
return center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果建筑位置不可用,在周围寻找
|
|
||||||
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, 2f, true))
|
|
||||||
{
|
|
||||||
if (cell.InBounds(map) && cell.Walkable(map))
|
|
||||||
{
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果都不可用,返回建筑位置
|
|
||||||
return center;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsValidTarget(Pawn pawn)
|
|
||||||
{
|
|
||||||
if (pawn == null || pawn.Downed || pawn.Dead || !pawn.Spawned)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 检查目标类型
|
|
||||||
if (Props.targetEnemies && pawn.HostileTo(parent.Faction))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (Props.targetNeutrals && !pawn.HostileTo(parent.Faction) && pawn.Faction != parent.Faction)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (Props.targetAnimals && pawn.RaceProps.Animal)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsInRange(IntVec3 position)
|
|
||||||
{
|
|
||||||
float distance = Vector3.Distance(parent.Position.ToVector3(), position.ToVector3());
|
|
||||||
return distance <= Props.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CompTick()
|
|
||||||
{
|
|
||||||
base.CompTick();
|
|
||||||
|
|
||||||
int currentTick = Find.TickManager.TicksGame;
|
|
||||||
|
|
||||||
// 检查是否需要更新状态
|
|
||||||
if (currentTick >= nextUpdateTick)
|
|
||||||
{
|
|
||||||
UpdateState();
|
|
||||||
nextUpdateTick = currentTick + Props.updateIntervalTicks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查EnergyLance状态
|
|
||||||
if (activeLance != null && activeLance.Destroyed)
|
|
||||||
{
|
|
||||||
activeLance = null;
|
|
||||||
if (currentState == EnergyLanceState.Tracking)
|
|
||||||
{
|
|
||||||
currentState = EnergyLanceState.Searching;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 外部调用:当EnergyLance需要目标时调用
|
|
||||||
public bool TryGetCurrentTarget(out IntVec3 targetPos)
|
|
||||||
{
|
|
||||||
if (currentTarget != null && IsValidTarget(currentTarget) && IsInRange(currentTarget.Position))
|
|
||||||
{
|
|
||||||
targetPos = currentTarget.Position;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
targetPos = IntVec3.Invalid;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void PostExposeData()
|
|
||||||
{
|
|
||||||
base.PostExposeData();
|
|
||||||
|
|
||||||
Scribe_Values.Look(ref currentState, "currentState", EnergyLanceState.Idle);
|
|
||||||
Scribe_Values.Look(ref nextUpdateTick, "nextUpdateTick", 0);
|
|
||||||
Scribe_Values.Look(ref noTargetTicks, "noTargetTicks", 0);
|
|
||||||
Scribe_References.Look(ref currentTarget, "currentTarget");
|
|
||||||
Scribe_Values.Look(ref lastTargetPosition, "lastTargetPosition", IntVec3.Invalid);
|
|
||||||
Scribe_References.Look(ref activeLance, "activeLance");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EnergyLanceState
|
|
||||||
{
|
|
||||||
Idle,
|
|
||||||
Searching,
|
|
||||||
Tracking,
|
|
||||||
NoTarget
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using RimWorld;
|
|
||||||
using Verse;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class CompProperties_BuildingEnergyLance : CompProperties
|
|
||||||
{
|
|
||||||
// 基础配置
|
|
||||||
public float radius = 20f; // 作用半径
|
|
||||||
public int updateIntervalTicks = 30; // 目标更新间隔(刻)
|
|
||||||
public int maxNoTargetTicks = 120; // 无目标时最大存活时间
|
|
||||||
|
|
||||||
// 目标选择配置
|
|
||||||
public bool targetEnemies = true; // 是否以敌人为目标
|
|
||||||
public bool targetNeutrals = false; // 是否以中立单位为目标
|
|
||||||
public bool targetAnimals = false; // 是否以动物为目标
|
|
||||||
|
|
||||||
// EnergyLance配置
|
|
||||||
public ThingDef energyLanceDef; // 使用的EnergyLance类型
|
|
||||||
public int energyLanceDuration = 600; // EnergyLance基础持续时间
|
|
||||||
public int firesPerTick = 3; // 每刻伤害次数
|
|
||||||
|
|
||||||
public CompProperties_BuildingEnergyLance()
|
|
||||||
{
|
|
||||||
this.compClass = typeof(CompBuildingEnergyLance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
using RimWorld;
|
|
||||||
using Verse;
|
|
||||||
using UnityEngine;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace WulaFallenEmpire
|
|
||||||
{
|
|
||||||
public class GuidedEnergyLance : OrbitalStrike
|
|
||||||
{
|
|
||||||
// 引导相关属性
|
|
||||||
public Thing controlBuilding; // 控制建筑
|
|
||||||
private IntVec3 currentTarget = IntVec3.Invalid; // 当前目标位置
|
|
||||||
private int lastTargetUpdateTick = -1; // 最后收到目标更新的刻
|
|
||||||
private int maxNoUpdateTicks = 60; // 无更新时的最大存活时间
|
|
||||||
|
|
||||||
// 移动配置
|
|
||||||
public float moveSpeed = 2.0f; // 移动速度(格/秒)
|
|
||||||
public float turnSpeed = 90f; // 转向速度(度/秒)
|
|
||||||
|
|
||||||
// 状态
|
|
||||||
private Vector3 currentVelocity;
|
|
||||||
private bool hasValidTarget = false;
|
|
||||||
|
|
||||||
// ModExtension引用
|
|
||||||
private EnergyLanceExtension extension;
|
|
||||||
|
|
||||||
private static List<Thing> tmpThings = new List<Thing>();
|
|
||||||
|
|
||||||
public override void StartStrike()
|
|
||||||
{
|
|
||||||
base.StartStrike();
|
|
||||||
|
|
||||||
// 获取ModExtension
|
|
||||||
extension = def.GetModExtension<EnergyLanceExtension>();
|
|
||||||
if (extension == null)
|
|
||||||
{
|
|
||||||
Log.Error($"[GuidedEnergyLance] No EnergyLanceExtension found on {def.defName}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTargetUpdateTick = Find.TickManager.TicksGame;
|
|
||||||
|
|
||||||
// 创建视觉效果
|
|
||||||
CreateVisualEffect();
|
|
||||||
|
|
||||||
Log.Message($"[GuidedEnergyLance] Guided EnergyLance started, controlled by {controlBuilding?.Label ?? "None"}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateVisualEffect()
|
|
||||||
{
|
|
||||||
// 使用ModExtension中定义的Mote,如果没有则使用默认的PowerBeam
|
|
||||||
if (extension.moteDef != null)
|
|
||||||
{
|
|
||||||
Mote mote = MoteMaker.MakeStaticMote(base.Position, base.Map, extension.moteDef);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 使用原版PowerBeam的视觉效果
|
|
||||||
MoteMaker.MakePowerBeamMote(base.Position, base.Map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Tick()
|
|
||||||
{
|
|
||||||
base.Tick();
|
|
||||||
|
|
||||||
if (!base.Destroyed && extension != null)
|
|
||||||
{
|
|
||||||
// 检查目标更新状态
|
|
||||||
CheckTargetStatus();
|
|
||||||
|
|
||||||
// 移动光束
|
|
||||||
MoveBeam();
|
|
||||||
|
|
||||||
// 造成伤害
|
|
||||||
for (int i = 0; i < firesPerTick; i++)
|
|
||||||
{
|
|
||||||
DoBeamDamage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckTargetStatus()
|
|
||||||
{
|
|
||||||
int currentTick = Find.TickManager.TicksGame;
|
|
||||||
int ticksSinceUpdate = currentTick - lastTargetUpdateTick;
|
|
||||||
|
|
||||||
// 检查是否长时间未收到更新
|
|
||||||
if (ticksSinceUpdate >= maxNoUpdateTicks)
|
|
||||||
{
|
|
||||||
Log.Message($"[GuidedEnergyLance] No target updates for {ticksSinceUpdate} ticks, destroying");
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查控制建筑状态
|
|
||||||
if (controlBuilding == null || controlBuilding.Destroyed || !controlBuilding.Spawned)
|
|
||||||
{
|
|
||||||
Log.Message($"[GuidedEnergyLance] Control building lost, destroying");
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveBeam()
|
|
||||||
{
|
|
||||||
if (!hasValidTarget || !currentTarget.IsValid)
|
|
||||||
{
|
|
||||||
// 没有有效目标,缓慢移动或保持原地
|
|
||||||
ApplyMinimalMovement();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算移动方向
|
|
||||||
Vector3 targetDirection = (currentTarget.ToVector3() - base.Position.ToVector3()).normalized;
|
|
||||||
|
|
||||||
// 平滑转向
|
|
||||||
if (currentVelocity.magnitude > 0.1f)
|
|
||||||
{
|
|
||||||
float maxTurnAngle = turnSpeed * 0.0167f; // 每帧最大转向角度(假设60FPS)
|
|
||||||
currentVelocity = Vector3.RotateTowards(currentVelocity, targetDirection, maxTurnAngle * Mathf.Deg2Rad, moveSpeed * 0.0167f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentVelocity = targetDirection * moveSpeed * 0.0167f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 应用移动
|
|
||||||
Vector3 newPos = base.Position.ToVector3() + currentVelocity;
|
|
||||||
IntVec3 newCell = new IntVec3(Mathf.RoundToInt(newPos.x), 0, Mathf.RoundToInt(newPos.z));
|
|
||||||
|
|
||||||
if (newCell != base.Position && newCell.InBounds(base.Map))
|
|
||||||
{
|
|
||||||
base.Position = newCell;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否接近目标
|
|
||||||
float distanceToTarget = Vector3.Distance(base.Position.ToVector3(), currentTarget.ToVector3());
|
|
||||||
if (distanceToTarget < 1.5f)
|
|
||||||
{
|
|
||||||
// 到达目标附近,可以减速或保持位置
|
|
||||||
currentVelocity *= 0.8f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Message($"[GuidedEnergyLance] Moving to {currentTarget}, distance: {distanceToTarget:F1}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyMinimalMovement()
|
|
||||||
{
|
|
||||||
// 无目标时的最小移动,防止完全静止
|
|
||||||
if (currentVelocity.magnitude < 0.1f)
|
|
||||||
{
|
|
||||||
// 随机轻微移动
|
|
||||||
currentVelocity = new Vector3(Rand.Range(-0.1f, 0.1f), 0f, Rand.Range(-0.1f, 0.1f));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 缓慢减速
|
|
||||||
currentVelocity *= 0.95f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 newPos = base.Position.ToVector3() + currentVelocity;
|
|
||||||
IntVec3 newCell = new IntVec3(Mathf.RoundToInt(newPos.x), 0, Mathf.RoundToInt(newPos.z));
|
|
||||||
|
|
||||||
if (newCell != base.Position && newCell.InBounds(base.Map))
|
|
||||||
{
|
|
||||||
base.Position = newCell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DoBeamDamage()
|
|
||||||
{
|
|
||||||
if (extension == null) return;
|
|
||||||
|
|
||||||
// 在当前光束位置周围随机选择一个单元格
|
|
||||||
IntVec3 targetCell = (from x in GenRadial.RadialCellsAround(base.Position, 2f, useCenter: true)
|
|
||||||
where x.InBounds(base.Map)
|
|
||||||
select x).RandomElementByWeight((IntVec3 x) => 1f - Mathf.Min(x.DistanceTo(base.Position) / 2f, 1f) + 0.05f);
|
|
||||||
|
|
||||||
// 尝试在该单元格点火(如果配置了点火)
|
|
||||||
if (extension.igniteFires)
|
|
||||||
{
|
|
||||||
FireUtility.TryStartFireIn(targetCell, base.Map, Rand.Range(0.1f, extension.fireIgniteChance), instigator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对该单元格内的物体造成伤害
|
|
||||||
tmpThings.Clear();
|
|
||||||
tmpThings.AddRange(targetCell.GetThingList(base.Map));
|
|
||||||
|
|
||||||
for (int i = 0; i < tmpThings.Count; i++)
|
|
||||||
{
|
|
||||||
Thing thing = tmpThings[i];
|
|
||||||
|
|
||||||
// 检查是否对尸体造成伤害
|
|
||||||
if (!extension.applyDamageToCorpses && thing is Corpse)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 计算伤害量
|
|
||||||
int damageAmount = (thing is Corpse) ?
|
|
||||||
extension.corpseDamageAmountRange.RandomInRange :
|
|
||||||
extension.damageAmountRange.RandomInRange;
|
|
||||||
|
|
||||||
Pawn pawn = thing as Pawn;
|
|
||||||
BattleLogEntry_DamageTaken battleLogEntry = null;
|
|
||||||
|
|
||||||
if (pawn != null)
|
|
||||||
{
|
|
||||||
battleLogEntry = new BattleLogEntry_DamageTaken(pawn, RulePackDefOf.DamageEvent_PowerBeam, instigator as Pawn);
|
|
||||||
Find.BattleLog.Add(battleLogEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用ModExtension中定义的伤害类型
|
|
||||||
DamageInfo damageInfo = new DamageInfo(extension.damageDef, damageAmount, 0f, -1f, instigator, null, weaponDef);
|
|
||||||
thing.TakeDamage(damageInfo).AssociateWithLog(battleLogEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpThings.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 外部调用:更新目标位置
|
|
||||||
public void UpdateTarget(IntVec3 newTarget)
|
|
||||||
{
|
|
||||||
lastTargetUpdateTick = Find.TickManager.TicksGame;
|
|
||||||
|
|
||||||
if (newTarget.IsValid && newTarget.InBounds(base.Map))
|
|
||||||
{
|
|
||||||
currentTarget = newTarget;
|
|
||||||
hasValidTarget = true;
|
|
||||||
|
|
||||||
Log.Message($"[GuidedEnergyLance] Target updated to {newTarget}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hasValidTarget = false;
|
|
||||||
currentTarget = IntVec3.Invalid;
|
|
||||||
|
|
||||||
Log.Message($"[GuidedEnergyLance] Target cleared");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ExposeData()
|
|
||||||
{
|
|
||||||
base.ExposeData();
|
|
||||||
|
|
||||||
Scribe_References.Look(ref controlBuilding, "controlBuilding");
|
|
||||||
Scribe_Values.Look(ref currentTarget, "currentTarget", IntVec3.Invalid);
|
|
||||||
Scribe_Values.Look(ref lastTargetUpdateTick, "lastTargetUpdateTick", -1);
|
|
||||||
Scribe_Values.Look(ref maxNoUpdateTicks, "maxNoUpdateTicks", 60);
|
|
||||||
Scribe_Values.Look(ref moveSpeed, "moveSpeed", 2.0f);
|
|
||||||
Scribe_Values.Look(ref turnSpeed, "turnSpeed", 90f);
|
|
||||||
Scribe_Values.Look(ref firesPerTick, "firesPerTick", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -76,8 +76,9 @@
|
|||||||
<Compile Include="Ability\WULA_AbilityBombardment\CompProperties_AbilityBombardment.cs" />
|
<Compile Include="Ability\WULA_AbilityBombardment\CompProperties_AbilityBombardment.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompAbilityEffect_CircularBombardment.cs" />
|
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompAbilityEffect_CircularBombardment.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompProperties_AbilityCircularBombardment.cs" />
|
<Compile Include="Ability\WULA_AbilityCircularBombardment\CompProperties_AbilityCircularBombardment.cs" />
|
||||||
|
<Compile Include="Ability\WULA_AbilityEnergyLance\AbilityWeaponDefExtension.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityEnergyLance\CompAbilityEffect_EnergyLance.cs" />
|
<Compile Include="Ability\WULA_AbilityEnergyLance\CompAbilityEffect_EnergyLance.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityEnergyLance\CompProperties_EnergyLance.cs" />
|
<Compile Include="Ability\WULA_AbilityEnergyLance\CompProperties_AbilityEnergyLance.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityEnergyLance\EnergyLance.cs" />
|
<Compile Include="Ability\WULA_AbilityEnergyLance\EnergyLance.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilityEnergyLance\EnergyLanceExtension.cs" />
|
<Compile Include="Ability\WULA_AbilityEnergyLance\EnergyLanceExtension.cs" />
|
||||||
<Compile Include="Ability\WULA_AbilitySurveillanceBombardment\CompAbilityEffect_CallSkyfaller.cs" />
|
<Compile Include="Ability\WULA_AbilitySurveillanceBombardment\CompAbilityEffect_CallSkyfaller.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user