11
This commit is contained in:
Binary file not shown.
@@ -200,7 +200,7 @@
|
||||
<fleckDef>ARA_Mote_Melee_Attack_Main</fleckDef>
|
||||
<burstCount>1</burstCount>
|
||||
<color>(170,74,68)</color>
|
||||
<scale>3.5~4.5</scale>
|
||||
<scale>6.5~7.5</scale>
|
||||
<speed>0.5</speed>
|
||||
<angle>-35~35</angle>
|
||||
<spawnLocType>OnSource</spawnLocType>
|
||||
@@ -211,7 +211,7 @@
|
||||
<fleckDef>ARA_Mote_Melee_Attack_Main</fleckDef>
|
||||
<burstCount>1</burstCount>
|
||||
<color>(147,50,28)</color>
|
||||
<scale>2.5~3.5</scale>
|
||||
<scale>6.5~7.5</scale>
|
||||
<speed>0.5</speed>
|
||||
<angle>-15~15</angle>
|
||||
<spawnLocType>OnSource</spawnLocType>
|
||||
@@ -227,7 +227,7 @@
|
||||
<fleckDef>ARA_Mote_Melee_Attack_Main</fleckDef>
|
||||
<burstCount>1</burstCount>
|
||||
<color>(170,74,68)</color>
|
||||
<scale>3.5~4.5</scale>
|
||||
<scale>6.5~7.5</scale>
|
||||
<speed>0.5</speed>
|
||||
<angle>180~196</angle>
|
||||
<spawnLocType>OnSource</spawnLocType>
|
||||
@@ -238,7 +238,7 @@
|
||||
<fleckDef>ARA_Mote_Melee_Attack_Main</fleckDef>
|
||||
<burstCount>1</burstCount>
|
||||
<color>(147,50,28)</color>
|
||||
<scale>2.5~3.5</scale>
|
||||
<scale>6.5~7.5</scale>
|
||||
<speed>0.5</speed>
|
||||
<angle>195~215</angle>
|
||||
<spawnLocType>OnSource</spawnLocType>
|
||||
@@ -249,6 +249,29 @@
|
||||
</children>
|
||||
<positionRadius>0.1</positionRadius>
|
||||
</EffecterDef>
|
||||
<EffecterDef>
|
||||
<defName>ARA_Area_Crush</defName>
|
||||
<children>
|
||||
<li>
|
||||
<subEffecterClass>SubEffecter_SprayerTriggered</subEffecterClass>
|
||||
<fleckDef>Fleck_BlastMechBandShockwave</fleckDef>
|
||||
<burstCount>1</burstCount>
|
||||
<spawnLocType>OnSource</spawnLocType>
|
||||
<absoluteAngle>true</absoluteAngle>
|
||||
<rotation>0~0</rotation>
|
||||
</li>
|
||||
<li>
|
||||
<subEffecterClass>SubEffecter_SprayerTriggered</subEffecterClass>
|
||||
<moteDef>Mote_GiantExplosion</moteDef>
|
||||
<scale>1~1</scale>
|
||||
</li>
|
||||
<li>
|
||||
<subEffecterClass>SubEffecter_SprayerTriggered</subEffecterClass>
|
||||
<moteDef>Mote_GiantExplosionInner</moteDef>
|
||||
<scale>5~5</scale>
|
||||
</li>
|
||||
</children>
|
||||
</EffecterDef>
|
||||
|
||||
<FleckDef ParentName="FleckBase">
|
||||
<defName>ARA_Fleck_Icez_Cloud</defName>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
</race>
|
||||
<tools Inherit="False">
|
||||
<li>
|
||||
<label>挥击</label>
|
||||
<label>镰爪挥击</label>
|
||||
<capacities>
|
||||
<li>Cut</li>
|
||||
<li>ARA_PawnBodyWeapon_Cleave</li>
|
||||
</capacities>
|
||||
<power>65</power>
|
||||
<armorPenetration>2</armorPenetration>
|
||||
@@ -23,14 +23,16 @@
|
||||
</li>
|
||||
</tools>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_Cleave">
|
||||
<cleaveAngle>120</cleaveAngle>
|
||||
<cleaveRange>4</cleaveRange>
|
||||
<cleaveDamageFactor>0.75</cleaveDamageFactor>
|
||||
<li Class="ArachnaeSwarm.CompProperties_PawnBodyWeapon">
|
||||
<cleaveAngle>60</cleaveAngle>
|
||||
<cleaveRange>6</cleaveRange>
|
||||
<cleaveDamageFactor>1</cleaveDamageFactor>
|
||||
<damageDowned>false</damageDowned>
|
||||
<explosionDamageDef>Cut</explosionDamageDef>
|
||||
<cleaveDamageDef>Cut</cleaveDamageDef>
|
||||
<attackEffecter>ARA_Double_Melee_Attack_Hit</attackEffecter>
|
||||
<cleaveEffecter>ARA_Double_Melee_Attack_Hit</cleaveEffecter>
|
||||
<requiresMeleeSkill>true</requiresMeleeSkill>
|
||||
<onlyWhenDrafted>false</onlyWhenDrafted>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AdvancedTraining">
|
||||
<trainables>
|
||||
@@ -67,6 +69,31 @@
|
||||
</trainables>
|
||||
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_AreaDamage">
|
||||
<radius>4</radius>
|
||||
<damageIntervalTicks>120</damageIntervalTicks>
|
||||
<damageDef>Crush</damageDef>
|
||||
<damageAmount>60</damageAmount>
|
||||
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
|
||||
<areaEffecterDef>ARA_Area_Crush</areaEffecterDef>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
<ToolCapacityDef>
|
||||
<defName>ARA_PawnBodyWeapon_Cleave</defName>
|
||||
<label>镰爪挥击</label>
|
||||
</ToolCapacityDef>
|
||||
<ManeuverDef>
|
||||
<defName>ARA_PawnBodyWeapon_Cleave</defName>
|
||||
<requiredCapacity>ARA_PawnBodyWeapon_Cleave</requiredCapacity>
|
||||
<verb>
|
||||
<verbClass>ArachnaeSwarm.Verb_MeleeAttack_BodyWeapon</verbClass>
|
||||
<meleeDamageDef>Cut</meleeDamageDef>
|
||||
</verb>
|
||||
<logEntryDef>MeleeAttack</logEntryDef>
|
||||
<combatLogRulesHit>Maneuver_Slash_MeleeHit</combatLogRulesHit>
|
||||
<combatLogRulesDeflect>Maneuver_Slash_MeleeDeflect</combatLogRulesDeflect>
|
||||
<combatLogRulesMiss>Maneuver_Slash_MeleeMiss</combatLogRulesMiss>
|
||||
<combatLogRulesDodge>Maneuver_Slash_MeleeDodge</combatLogRulesDodge>
|
||||
</ManeuverDef>
|
||||
</Defs>
|
||||
@@ -215,8 +215,8 @@
|
||||
</cocoonDefs>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_Cleave">
|
||||
<cleaveAngle>90</cleaveAngle>
|
||||
<cleaveRange>2.5</cleaveRange>
|
||||
<cleaveAngle>30</cleaveAngle>
|
||||
<cleaveRange>3.5</cleaveRange>
|
||||
<cleaveDamageFactor>0.5</cleaveDamageFactor>
|
||||
<damageDowned>false</damageDowned>
|
||||
<explosionDamageDef>Cut</explosionDamageDef>
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
<label>光滑的阿拉克涅甲壳墙</label>
|
||||
<description>阿拉克涅工蜂将硬质材料和甲壳素混合堆起来形成的墙壁,虫族对这片墙壁进行了精心打磨,质地坚硬的同时看起来美观多了。</description>
|
||||
<uiIconPath>Things/Building/Linked/WallSmooth_MenuIcon</uiIconPath>
|
||||
<designationCategory IsNull="True" Inherit="False"/>
|
||||
<graphicData>
|
||||
<texPath>Things/Building/Linked/RockSmooth_Atlas</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
@@ -347,6 +348,7 @@
|
||||
<altitudeLayer>DoorMoveable</altitudeLayer>
|
||||
<fillPercent>1</fillPercent>
|
||||
<useHitPoints>true</useHitPoints>
|
||||
<designationCategory IsNull="True" Inherit="False"/>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Building/Door/ARA_InsectDoor</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
|
||||
@@ -407,6 +407,7 @@
|
||||
<renderPrecedence>250</renderPrecedence>
|
||||
<texturePath>ArachnaeSwarm/Terrain/Surfaces/ARA_InsectJelly_Terrain</texturePath>
|
||||
<burnedDef>ARA_InsectCreep</burnedDef>
|
||||
<designationCategory IsNull="True" Inherit="False"/>
|
||||
<color>(231, 224, 188)</color>
|
||||
<pollutionOverlayTexturePath>Terrain/Surfaces/AncientMegastructure</pollutionOverlayTexturePath>
|
||||
<pollutionShaderType MayRequire="Ludeon.RimWorld.Biotech">TerrainFadeRoughSoftLight</pollutionShaderType>
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -211,6 +211,10 @@
|
||||
<Compile Include="Storyteller\RaidWavePoolDef.cs" />
|
||||
<Compile Include="Flyover\ThingclassFlyOver.cs" />
|
||||
<Compile Include="Flyover\ARA_FlyOverDropPod\CompProperties_FlyOverDropPod.cs" />
|
||||
<Compile Include="Thing_Comps\ARA_AreaaDamage\CompAreaDamage.cs" />
|
||||
<Compile Include="Thing_Comps\ARA_AreaaDamage\CompProperties_AreaDamage.cs" />
|
||||
<Compile Include="Verbs\PawnBodyWeapon\CompProperties_PawnBodyWeapon.cs" />
|
||||
<Compile Include="Verbs\PawnBodyWeapon\Verb_MeleeAttack_BodyWeapon.cs" />
|
||||
<Compile Include="Verbs\Verb_ShootSelfUnderfoot.cs" />
|
||||
<Compile Include="Verbs\Verb_ShootWithOffset.cs" />
|
||||
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompAbilityEffect_AbilityShowTemperatureRange.cs" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,470 @@
|
||||
// File: CompAreaDamage.cs
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompAreaDamage : ThingComp
|
||||
{
|
||||
private int ticksUntilNextDamage;
|
||||
private bool enabled;
|
||||
private List<Effecter> activeEffecters = new List<Effecter>(); // 正在运行的效果
|
||||
private int effecterCleanupTick = -1; // 效果清理时间点
|
||||
|
||||
public CompProperties_AreaDamage Props => (CompProperties_AreaDamage)props;
|
||||
public bool Enabled => enabled;
|
||||
|
||||
public override void Initialize(CompProperties props)
|
||||
{
|
||||
base.Initialize(props);
|
||||
ticksUntilNextDamage = Props.damageIntervalTicks;
|
||||
enabled = Props.startEnabled;
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
if (!parent.Spawned || !enabled)
|
||||
return;
|
||||
|
||||
// 清理过期的效果
|
||||
if (activeEffecters.Count > 0 && Find.TickManager.TicksGame >= effecterCleanupTick)
|
||||
{
|
||||
CleanupOldEffecters();
|
||||
}
|
||||
|
||||
ticksUntilNextDamage--;
|
||||
if (ticksUntilNextDamage <= 0)
|
||||
{
|
||||
DoAreaDamage();
|
||||
PlayEffecter();
|
||||
ticksUntilNextDamage = Props.damageIntervalTicks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理旧的效果
|
||||
/// </summary>
|
||||
private void CleanupOldEffecters()
|
||||
{
|
||||
for (int i = activeEffecters.Count - 1; i >= 0; i--)
|
||||
{
|
||||
try
|
||||
{
|
||||
activeEffecters[i].Cleanup();
|
||||
}
|
||||
catch { }
|
||||
activeEffecters.RemoveAt(i);
|
||||
}
|
||||
effecterCleanupTick = -1;
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
enabled = !enabled;
|
||||
}
|
||||
|
||||
public void SetEnabled(bool newState)
|
||||
{
|
||||
enabled = newState;
|
||||
}
|
||||
|
||||
private void DoAreaDamage()
|
||||
{
|
||||
Map map = parent.Map;
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
// 获取范围内的所有物体
|
||||
List<Thing> thingsInRange = new List<Thing>();
|
||||
foreach (IntVec3 cell in GenRadial.RadialCellsAround(parent.Position, Props.radius, true))
|
||||
{
|
||||
if (!cell.InBounds(map))
|
||||
continue;
|
||||
|
||||
List<Thing> thingList = cell.GetThingList(map);
|
||||
foreach (Thing thing in thingList)
|
||||
{
|
||||
if (IsValidTargetType(thing) && IsValidTarget(thing) && !thingsInRange.Contains(thing))
|
||||
{
|
||||
thingsInRange.Add(thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对每个有效目标造成伤害
|
||||
foreach (Thing target in thingsInRange)
|
||||
{
|
||||
ApplyDamageToTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放区域效果
|
||||
/// </summary>
|
||||
private void PlayEffecter()
|
||||
{
|
||||
if (Props.areaEffecterDef == null)
|
||||
return;
|
||||
|
||||
Map map = parent.Map;
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
// 播放音效
|
||||
if (Props.effectSoundDef != null)
|
||||
{
|
||||
Props.effectSoundDef.PlayOneShot(new TargetInfo(parent.Position, map));
|
||||
}
|
||||
|
||||
// 在中心位置播放效果
|
||||
if (Props.effectAtCenter)
|
||||
{
|
||||
PlayEffectAtPosition(parent.Position, map);
|
||||
}
|
||||
|
||||
// 如果需要,在每个目标位置播放效果
|
||||
if (Props.effectAtTargets || Props.multipleEffects)
|
||||
{
|
||||
var targets = GetValidTargetsInRange();
|
||||
foreach (var target in targets)
|
||||
{
|
||||
PlayEffectAtPosition(target.Position, map);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置效果清理时间
|
||||
effecterCleanupTick = Find.TickManager.TicksGame + Props.effecterDurationTicks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定位置播放效果
|
||||
/// </summary>
|
||||
private void PlayEffectAtPosition(IntVec3 position, Map map)
|
||||
{
|
||||
try
|
||||
{
|
||||
Effecter effecter = Props.areaEffecterDef.Spawn();
|
||||
activeEffecters.Add(effecter);
|
||||
|
||||
// 设置效果缩放
|
||||
float scale = Props.effectScaleRange.RandomInRange;
|
||||
// 注意:Effecter的缩放需要特殊的处理方式,这里使用反射或组件
|
||||
|
||||
// 触发效果
|
||||
TargetInfo sourceInfo = new TargetInfo(position, map);
|
||||
effecter.Trigger(sourceInfo, sourceInfo);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Failed to spawn effecter at {position}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查目标是否为建筑或Pawn
|
||||
/// </summary>
|
||||
private bool IsValidTargetType(Thing thing)
|
||||
{
|
||||
// 只针对建筑和Pawn
|
||||
return thing is Building || thing is Pawn;
|
||||
}
|
||||
|
||||
private bool IsValidTarget(Thing thing)
|
||||
{
|
||||
// 首先检查是否为建筑或Pawn(双重检查)
|
||||
if (!(thing is Building || thing is Pawn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否为 Pawn
|
||||
if (thing is Pawn pawn)
|
||||
{
|
||||
return IsValidPawnTarget(pawn);
|
||||
}
|
||||
// 检查是否为建筑
|
||||
else if (thing is Building building)
|
||||
{
|
||||
return IsValidBuildingTarget(building);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查Pawn是否有效目标
|
||||
/// </summary>
|
||||
private bool IsValidPawnTarget(Pawn pawn)
|
||||
{
|
||||
// 基础检查:死亡或倒地的Pawn不是有效目标
|
||||
if (pawn.Dead || pawn.Downed)
|
||||
return false;
|
||||
|
||||
// 检查是否影响Pawn
|
||||
if (!Props.affectPawns)
|
||||
return false;
|
||||
|
||||
// 检查阵营关系
|
||||
return CheckFactionRelationship(pawn.Faction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查建筑是否有效目标
|
||||
/// </summary>
|
||||
private bool IsValidBuildingTarget(Building building)
|
||||
{
|
||||
// 基础检查:建筑必须有生命值且未损坏
|
||||
if (building.def.useHitPoints == false || building.HitPoints <= 0)
|
||||
return false;
|
||||
|
||||
// 检查是否影响建筑
|
||||
if (!Props.affectBuildings)
|
||||
return false;
|
||||
|
||||
// 检查阵营关系
|
||||
return CheckFactionRelationship(building.Faction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查阵营关系
|
||||
/// </summary>
|
||||
private bool CheckFactionRelationship(Faction targetFaction)
|
||||
{
|
||||
Faction parentFaction = parent.Faction;
|
||||
|
||||
// 如果忽略所有阵营关系检查,直接返回true
|
||||
if (Props.ignoreFactionRelations)
|
||||
return true;
|
||||
|
||||
// 如果影响所有物体,直接返回true
|
||||
if (Props.affectEverything)
|
||||
return true;
|
||||
|
||||
// 父物体没有派系的情况
|
||||
if (parentFaction == null)
|
||||
{
|
||||
// 目标也没有派系 - 检查是否影响中立
|
||||
if (targetFaction == null)
|
||||
return Props.affectNeutral;
|
||||
|
||||
// 目标是玩家 - 检查是否影响友好
|
||||
if (targetFaction.IsPlayer)
|
||||
return Props.affectFriendly;
|
||||
|
||||
// 目标是非玩家派系 - 检查是否影响敌对
|
||||
return Props.affectHostile;
|
||||
}
|
||||
|
||||
// 父物体有派系的情况
|
||||
if (targetFaction == null)
|
||||
{
|
||||
// 目标没有派系 - 检查是否影响中立
|
||||
return Props.affectNeutral;
|
||||
}
|
||||
|
||||
// 目标与父物体同派系 - 检查是否影响友好
|
||||
if (targetFaction == parentFaction)
|
||||
return Props.affectFriendly;
|
||||
|
||||
// 目标与父物体敌对 - 检查是否影响敌对
|
||||
if (targetFaction.HostileTo(parentFaction))
|
||||
return Props.affectHostile;
|
||||
|
||||
// 其他情况视为中立 - 检查是否影响中立
|
||||
return Props.affectNeutral;
|
||||
}
|
||||
|
||||
private void ApplyDamageToTarget(Thing target)
|
||||
{
|
||||
if (Props.damageDef == null)
|
||||
return;
|
||||
|
||||
// 计算最终伤害量(应用缩放)
|
||||
int finalDamageAmount = CalculateFinalDamage(target);
|
||||
|
||||
// 创建伤害信息
|
||||
DamageInfo damageInfo = new DamageInfo(
|
||||
Props.damageDef,
|
||||
finalDamageAmount,
|
||||
armorPenetration: Props.armorPenetration,
|
||||
instigator: parent,
|
||||
hitPart: null,
|
||||
weapon: null,
|
||||
category: DamageInfo.SourceCategory.ThingOrUnknown
|
||||
);
|
||||
|
||||
// 应用伤害
|
||||
target.TakeDamage(damageInfo);
|
||||
|
||||
// 特殊效果处理
|
||||
HandleSpecialEffects(target, damageInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算最终伤害量,应用心灵敏感度缩放和保底伤害
|
||||
/// </summary>
|
||||
private int CalculateFinalDamage(Thing target)
|
||||
{
|
||||
float damageFactor = 1.0f;
|
||||
|
||||
// 使用固定缩放值
|
||||
if (Props.useFixedScaling)
|
||||
{
|
||||
damageFactor = Props.fixedDamageFactor;
|
||||
}
|
||||
// 使用心灵敏感度缩放(仅对Pawn有效)
|
||||
else if (Props.scaleWithPsychicSensitivity && target is Pawn pawn)
|
||||
{
|
||||
damageFactor = CalculatePsychicSensitivityFactor(pawn);
|
||||
}
|
||||
|
||||
// 确保伤害倍率在最小和最大范围内
|
||||
damageFactor = Mathf.Clamp(damageFactor, Props.minDamageFactor, Props.maxDamageFactor);
|
||||
|
||||
// 计算最终伤害
|
||||
int finalDamage = Mathf.RoundToInt(Props.damageAmount * damageFactor);
|
||||
|
||||
// 确保至少造成1点伤害
|
||||
return Mathf.Max(1, finalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据目标的心灵敏感度计算伤害倍率
|
||||
/// </summary>
|
||||
private float CalculatePsychicSensitivityFactor(Pawn targetPawn)
|
||||
{
|
||||
// 获取心灵敏感度(如果目标没有心灵敏感度,使用默认值0.5)
|
||||
float psychicSensitivity = 0.5f;
|
||||
|
||||
if (targetPawn.health != null && targetPawn.health.capacities != null)
|
||||
{
|
||||
psychicSensitivity = targetPawn.GetStatValue(StatDefOf.PsychicSensitivity);
|
||||
}
|
||||
|
||||
// 返回心灵敏感度作为伤害倍率
|
||||
return psychicSensitivity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理特殊效果(如伤害类型特定的效果)
|
||||
/// </summary>
|
||||
private void HandleSpecialEffects(Thing target, DamageInfo damageInfo)
|
||||
{
|
||||
// 显示伤害数值(调试用)
|
||||
if (Props.showDamageNumbers)
|
||||
{
|
||||
MoteMaker.ThrowText(target.DrawPos, target.Map, damageInfo.Amount.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look(ref ticksUntilNextDamage, "ticksUntilNextDamage", Props.damageIntervalTicks);
|
||||
Scribe_Values.Look(ref enabled, "enabled", Props.startEnabled);
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||
{
|
||||
// 只有拥有者可以操作开关
|
||||
if (parent.Faction != null && parent.Faction != Faction.OfPlayer && !parent.Faction.IsPlayer)
|
||||
yield break;
|
||||
|
||||
// 创建切换开关的 Gizmo
|
||||
Command_Toggle toggleCommand = new Command_Toggle
|
||||
{
|
||||
defaultLabel = Props.toggleLabel.Translate(),
|
||||
defaultDesc = Props.toggleDescription.Translate(),
|
||||
icon = LoadToggleIcon(),
|
||||
isActive = () => enabled,
|
||||
toggleAction = () => Toggle()
|
||||
};
|
||||
|
||||
yield return toggleCommand;
|
||||
}
|
||||
|
||||
private Texture2D LoadToggleIcon()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Props.toggleIconPath))
|
||||
{
|
||||
return ContentFinder<Texture2D>.Get(Props.toggleIconPath, false);
|
||||
}
|
||||
|
||||
// 默认图标
|
||||
return TexCommand.DesirePower;
|
||||
}
|
||||
|
||||
public override string CompInspectStringExtra()
|
||||
{
|
||||
string baseString = base.CompInspectStringExtra();
|
||||
|
||||
// 状态信息
|
||||
string statusText = enabled ?
|
||||
"AreaDamageEnabled".Translate() :
|
||||
"AreaDamageDisabled".Translate();
|
||||
|
||||
if (string.IsNullOrEmpty(baseString))
|
||||
return statusText;
|
||||
else
|
||||
return baseString + "\n" + statusText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取范围内所有有效目标(调试和外部调用用)
|
||||
/// </summary>
|
||||
public List<Thing> GetValidTargetsInRange()
|
||||
{
|
||||
Map map = parent.Map;
|
||||
List<Thing> validTargets = new List<Thing>();
|
||||
|
||||
if (map == null || !enabled)
|
||||
return validTargets;
|
||||
|
||||
foreach (IntVec3 cell in GenRadial.RadialCellsAround(parent.Position, Props.radius, true))
|
||||
{
|
||||
if (!cell.InBounds(map))
|
||||
continue;
|
||||
|
||||
List<Thing> thingList = cell.GetThingList(map);
|
||||
foreach (Thing thing in thingList)
|
||||
{
|
||||
if (IsValidTargetType(thing) && IsValidTarget(thing) && !validTargets.Contains(thing))
|
||||
{
|
||||
validTargets.Add(thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validTargets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动触发一次区域伤害(用于外部调用)
|
||||
/// </summary>
|
||||
public void TriggerAreaDamage()
|
||||
{
|
||||
if (parent.Spawned && enabled)
|
||||
{
|
||||
DoAreaDamage();
|
||||
PlayEffecter();
|
||||
ticksUntilNextDamage = Props.damageIntervalTicks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制效果范围(用于调试或显示)
|
||||
/// </summary>
|
||||
public void DrawEffectRange()
|
||||
{
|
||||
if (parent.Spawned && enabled)
|
||||
{
|
||||
GenDraw.DrawRadiusRing(parent.Position, Props.radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// File: CompProperties_AreaDamage.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_AreaDamage : CompProperties
|
||||
{
|
||||
public float radius = 5f; // A: 伤害半径
|
||||
public int damageIntervalTicks = 60; // B: 伤害间隔(帧数)
|
||||
public DamageDef damageDef; // C: 伤害类型
|
||||
public int damageAmount = 10; // 基础伤害量
|
||||
public float armorPenetration = 0f; // 护甲穿透
|
||||
|
||||
// === 新增:Effecter设置 ===
|
||||
public EffecterDef areaEffecterDef = null; // 区域效果定义
|
||||
public int effecterDurationTicks = 30; // 效果持续时间(ticks)
|
||||
public SoundDef effectSoundDef = null; // 效果音效
|
||||
public FloatRange effectScaleRange = new FloatRange(0.8f, 1.2f); // 效果缩放范围
|
||||
|
||||
// 效果播放位置
|
||||
public bool effectAtTargets = false; // 效果是否在目标位置播放
|
||||
public bool effectAtCenter = true; // 效果是否在中心位置播放
|
||||
public bool multipleEffects = false; // 是否对每个目标播放单独效果
|
||||
|
||||
// 伤害缩放设置
|
||||
public bool scaleWithPsychicSensitivity = false; // 是否随心灵敏感度缩放
|
||||
public float minDamageFactor = 0.5f; // 最低伤害倍率(0.0-1.0)
|
||||
public float maxDamageFactor = 2.0f; // 最高伤害倍率
|
||||
public bool useFixedScaling = false; // 是否使用固定缩放值
|
||||
public float fixedDamageFactor = 1.0f; // 固定伤害倍率
|
||||
|
||||
// 目标过滤
|
||||
public bool affectFriendly = false; // 是否影响友方
|
||||
public bool affectNeutral = true; // 是否影响中立
|
||||
public bool affectHostile = true; // 是否影响敌方
|
||||
public bool affectBuildings = true; // 是否影响建筑
|
||||
public bool affectPawns = true; // 是否影响生物
|
||||
|
||||
// 特殊设置
|
||||
public bool ignoreFactionRelations = false; // 忽略所有阵营关系检查(用于无派系实体)
|
||||
public bool affectEverything = false; // 影响范围内所有有生命值的物体
|
||||
|
||||
// 特殊效果
|
||||
public bool showDamageNumbers = false; // 显示伤害数值(调试用)
|
||||
|
||||
// 开关设置
|
||||
public bool startEnabled = true; // 初始是否启用
|
||||
public string toggleLabel = "Toggle Area Damage"; // 开关标签
|
||||
public string toggleDescription = "Enable or disable the area damage effect"; // 开关描述
|
||||
public string toggleIconPath; // 开关图标路径
|
||||
|
||||
public CompProperties_AreaDamage()
|
||||
{
|
||||
compClass = typeof(CompAreaDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// File: CompPawnBodyWeapon.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_PawnBodyWeapon : CompProperties
|
||||
{
|
||||
// 基础伤害设置
|
||||
public float cleaveAngle = 90f;
|
||||
public float cleaveRange = 2.5f;
|
||||
public float cleaveDamageFactor = 0.7f;
|
||||
public bool damageDowned = false;
|
||||
public DamageDef cleaveDamageDef = null;
|
||||
|
||||
// 攻击效果
|
||||
public EffecterDef attackEffecter = null;
|
||||
public EffecterDef cleaveEffecter = null;
|
||||
public SoundDef attackSound = null;
|
||||
|
||||
// 特殊效果
|
||||
public HediffDef applyHediffOnHit = null; // 命中时附加的效果
|
||||
public float hediffSeverity = 0.1f;
|
||||
public float hediffChance = 1.0f;
|
||||
|
||||
// 条件触发
|
||||
public bool requiresMeleeSkill = false; // 是否需要近战技能
|
||||
public int minMeleeSkillLevel = 0;
|
||||
public bool onlyWhenDrafted = false; // 是否只在征召时生效
|
||||
|
||||
public CompProperties_PawnBodyWeapon()
|
||||
{
|
||||
this.compClass = typeof(CompPawnBodyWeapon);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompPawnBodyWeapon : ThingComp
|
||||
{
|
||||
public CompProperties_PawnBodyWeapon Props => (CompProperties_PawnBodyWeapon)this.props;
|
||||
|
||||
private Pawn Pawn => this.parent as Pawn;
|
||||
|
||||
// 检查是否可以使用身体武器
|
||||
public bool CanUseBodyWeapon(Verb verb = null)
|
||||
{
|
||||
if (Pawn == null || Pawn.Dead || Pawn.Downed)
|
||||
return false;
|
||||
|
||||
// 检查是否被征召
|
||||
if (Props.onlyWhenDrafted && !Pawn.Drafted)
|
||||
return false;
|
||||
|
||||
// 检查近战技能
|
||||
if (Props.requiresMeleeSkill && Pawn.skills != null)
|
||||
{
|
||||
var meleeSkill = Pawn.skills.GetSkill(SkillDefOf.Melee);
|
||||
if (meleeSkill.Level < Props.minMeleeSkillLevel)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取实际伤害系数(可以基于技能、状态等调整)
|
||||
public float GetDamageFactor(Verb verb = null)
|
||||
{
|
||||
float factor = Props.cleaveDamageFactor;
|
||||
|
||||
// 如果有近战技能加成
|
||||
if (Pawn.skills != null)
|
||||
{
|
||||
var meleeSkill = Pawn.skills.GetSkill(SkillDefOf.Melee);
|
||||
factor *= (1.0f + (meleeSkill.Level * 0.02f)); // 每级增加2%伤害
|
||||
}
|
||||
|
||||
// 检查是否有增强状态
|
||||
if (Pawn.health?.hediffSet != null)
|
||||
{
|
||||
// 这里可以添加基于hediff的伤害加成
|
||||
// 例如:狂暴状态增加伤害
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
// 获取攻击范围
|
||||
public float GetCleaveRange(Verb verb = null)
|
||||
{
|
||||
float range = Props.cleaveRange;
|
||||
|
||||
// 基于体型调整范围
|
||||
if (Pawn.BodySize > 1.0f)
|
||||
{
|
||||
range *= Pawn.BodySize * 0.5f;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
// 获取攻击角度
|
||||
public float GetCleaveAngle(Verb verb = null)
|
||||
{
|
||||
return Props.cleaveAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// File: Verb_MeleeAttack_BodyWeapon.cs
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class Verb_MeleeAttack_BodyWeapon : Verb_MeleeAttack
|
||||
{
|
||||
private CompPawnBodyWeapon Comp
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.CasterPawn?.GetComp<CompPawnBodyWeapon>();
|
||||
}
|
||||
}
|
||||
|
||||
protected override DamageWorker.DamageResult ApplyMeleeDamageToTarget(LocalTargetInfo target)
|
||||
{
|
||||
DamageWorker.DamageResult result = new DamageWorker.DamageResult();
|
||||
|
||||
// 播放攻击特效
|
||||
PlayAttackEffecter(target);
|
||||
|
||||
// 1. 对主目标造成伤害
|
||||
DamageInfo dinfo = new DamageInfo(
|
||||
this.verbProps.meleeDamageDef,
|
||||
this.verbProps.AdjustedMeleeDamageAmount(this, this.CasterPawn),
|
||||
this.verbProps.AdjustedArmorPenetration(this, this.CasterPawn),
|
||||
-1f,
|
||||
this.CasterPawn,
|
||||
null,
|
||||
null // 身体武器没有装备
|
||||
);
|
||||
dinfo.SetTool(this.tool);
|
||||
|
||||
if (target.HasThing)
|
||||
{
|
||||
result = target.Thing.TakeDamage(dinfo);
|
||||
|
||||
// 附加特殊效果
|
||||
ApplySpecialEffects(target.Thing);
|
||||
}
|
||||
|
||||
// 2. 执行溅射伤害
|
||||
Pawn casterPawn = this.CasterPawn;
|
||||
if (casterPawn == null || !target.HasThing)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Thing mainTarget = target.Thing;
|
||||
Vector3 attackDirection = (mainTarget.Position - casterPawn.Position).ToVector3().normalized;
|
||||
bool mainTargetIsHostile = mainTarget.HostileTo(casterPawn);
|
||||
|
||||
// 获取溅射参数
|
||||
float cleaveRange = this.Comp.GetCleaveRange(this);
|
||||
float cleaveAngle = this.Comp.GetCleaveAngle(this);
|
||||
float damageFactor = this.Comp.GetDamageFactor(this);
|
||||
|
||||
// 查找施法者周围的潜在目标
|
||||
IEnumerable<Thing> potentialTargets = GenRadial.RadialDistinctThingsAround(
|
||||
casterPawn.Position,
|
||||
casterPawn.Map,
|
||||
cleaveRange,
|
||||
useCenter: true
|
||||
);
|
||||
|
||||
foreach (Thing thing in potentialTargets)
|
||||
{
|
||||
// 跳过主目标、自己和非生物
|
||||
if (thing == mainTarget || thing == casterPawn || !(thing is Pawn secondaryTargetPawn))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据XML配置决定是否跳过倒地的生物
|
||||
if (!this.Comp.Props.damageDowned && secondaryTargetPawn.Downed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 智能溅射:次要目标的敌对状态必须与主目标一致
|
||||
if (secondaryTargetPawn.HostileTo(casterPawn) != mainTargetIsHostile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查目标是否在攻击扇形范围内
|
||||
Vector3 directionToTarget = (thing.Position - casterPawn.Position).ToVector3().normalized;
|
||||
float angle = Vector3.Angle(attackDirection, directionToTarget);
|
||||
|
||||
if (angle <= cleaveAngle / 2f)
|
||||
{
|
||||
// 对次要目标造成伤害
|
||||
DamageInfo cleaveDinfo = new DamageInfo(
|
||||
this.verbProps.meleeDamageDef,
|
||||
this.verbProps.AdjustedMeleeDamageAmount(this, casterPawn) * damageFactor,
|
||||
this.verbProps.AdjustedArmorPenetration(this, casterPawn) * damageFactor,
|
||||
-1f,
|
||||
casterPawn,
|
||||
null,
|
||||
null // 身体武器没有装备
|
||||
);
|
||||
cleaveDinfo.SetTool(this.tool);
|
||||
secondaryTargetPawn.TakeDamage(cleaveDinfo);
|
||||
|
||||
// 附加特殊效果
|
||||
ApplySpecialEffects(secondaryTargetPawn);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放攻击特效
|
||||
/// </summary>
|
||||
private void PlayAttackEffecter(LocalTargetInfo target)
|
||||
{
|
||||
if (this.CasterPawn == null || this.CasterPawn.Map == null || this.Comp == null)
|
||||
return;
|
||||
|
||||
// 播放攻击特效
|
||||
if (this.Comp.Props.attackEffecter != null)
|
||||
{
|
||||
Effecter attackEffect = this.Comp.Props.attackEffecter.Spawn();
|
||||
attackEffect.Trigger(new TargetInfo(this.CasterPawn.Position, this.CasterPawn.Map), target.ToTargetInfo(this.CasterPawn.Map));
|
||||
attackEffect.Cleanup();
|
||||
}
|
||||
|
||||
// 播放溅射特效
|
||||
if (this.Comp.Props.cleaveEffecter != null && target.HasThing)
|
||||
{
|
||||
PlayCleaveEffecter(target.Thing);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放溅射特效
|
||||
/// </summary>
|
||||
private void PlayCleaveEffecter(Thing mainTarget)
|
||||
{
|
||||
if (this.CasterPawn == null || this.CasterPawn.Map == null || mainTarget == null || this.Comp == null)
|
||||
return;
|
||||
|
||||
Pawn casterPawn = this.CasterPawn;
|
||||
|
||||
// 播放主要的溅射特效
|
||||
Effecter cleaveEffect = this.Comp.Props.cleaveEffecter.Spawn();
|
||||
cleaveEffect.Trigger(new TargetInfo(casterPawn.Position, casterPawn.Map), new TargetInfo(mainTarget.Position, casterPawn.Map));
|
||||
cleaveEffect.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 附加特殊效果
|
||||
/// </summary>
|
||||
private void ApplySpecialEffects(Thing target)
|
||||
{
|
||||
if (this.Comp == null || this.Comp.Props.applyHediffOnHit == null)
|
||||
return;
|
||||
|
||||
if (target is Pawn targetPawn)
|
||||
{
|
||||
if (Rand.Chance(this.Comp.Props.hediffChance))
|
||||
{
|
||||
Hediff hediff = HediffMaker.MakeHediff(this.Comp.Props.applyHediffOnHit, targetPawn);
|
||||
hediff.Severity = this.Comp.Props.hediffSeverity;
|
||||
targetPawn.health.AddHediff(hediff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawHighlight(LocalTargetInfo target)
|
||||
{
|
||||
base.DrawHighlight(target);
|
||||
|
||||
if (target.IsValid && CasterPawn != null && this.Comp != null && this.Comp.CanUseBodyWeapon(this))
|
||||
{
|
||||
GenDraw.DrawFieldEdges(GetCleaveCells(target.Cell));
|
||||
}
|
||||
}
|
||||
|
||||
private List<IntVec3> GetCleaveCells(IntVec3 center)
|
||||
{
|
||||
if (this.Comp == null || !this.Comp.CanUseBodyWeapon(this))
|
||||
{
|
||||
return new List<IntVec3>();
|
||||
}
|
||||
|
||||
IntVec3 casterPos = this.CasterPawn.Position;
|
||||
Map map = this.CasterPawn.Map;
|
||||
Vector3 attackDirection = (center - casterPos).ToVector3().normalized;
|
||||
|
||||
float cleaveRange = this.Comp.GetCleaveRange(this);
|
||||
float cleaveAngle = this.Comp.GetCleaveAngle(this);
|
||||
|
||||
return GenRadial.RadialCellsAround(casterPos, cleaveRange, useCenter: true)
|
||||
.Where(cell => {
|
||||
if (!cell.InBounds(map)) return false;
|
||||
Vector3 directionToCell = (cell - casterPos).ToVector3();
|
||||
if (directionToCell.sqrMagnitude <= 0.001f) return false; // 排除施法者自身位置
|
||||
return Vector3.Angle(attackDirection, directionToCell) <= cleaveAngle / 2f;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// 重写此方法以在攻击前检查身体武器状态
|
||||
protected override bool TryCastShot()
|
||||
{
|
||||
if (this.Comp != null && !this.Comp.CanUseBodyWeapon(this))
|
||||
{
|
||||
// 不能使用身体武器,使用普通攻击
|
||||
if (this.CasterPawn != null)
|
||||
{
|
||||
// 播放效果(可选)
|
||||
Messages.Message("ARA_BodyWeapon_CannotUse".Translate(this.CasterPawn.LabelShortCap),
|
||||
MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
return base.TryCastShot();
|
||||
}
|
||||
|
||||
return base.TryCastShot();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user