This commit is contained in:
2025-12-18 17:30:36 +08:00
parent 6a938abcb7
commit ad42832aa7
14 changed files with 1809 additions and 586 deletions

Binary file not shown.

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}