diff --git a/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/Assemblies/WulaFallenEmpire.dll
index dda1f49b..7b2e5af3 100644
Binary files a/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/1.6/Defs/PsychicRitualDefs/WULA_FallenEmpire_Rituals.xml b/1.6/Defs/PsychicRitualDefs/WULA_FallenEmpire_Rituals.xml
new file mode 100644
index 00000000..5a80c1ee
--- /dev/null
+++ b/1.6/Defs/PsychicRitualDefs/WULA_FallenEmpire_Rituals.xml
@@ -0,0 +1,86 @@
+
+
+
+
+ WULA_Ritual_TechOffering
+
+ 乌拉帝国用于交换失落技术的灵能仪式。通过献上高价值的科技产品,帝国可以从时空的裂隙中获得罕见的武器或工具。
+ 1800
+ 15
+ BasicPsychicRituals
+ UI/PsychicRituals/PsychicRitual_Default
+
+
+
+
+ TechprofSubpersonaCore
+
+
+ 1
+
+
+
+
+ Gold
+ 0.005
+
+
+ Plasteel
+ 0.01
+
+
+ Uranium
+ 0.015
+
+
+ ComponentSpacer
+ 0.05
+
+
+ TechprofSubpersonaCore
+ 0.2
+
+
+
+
+ WULA_MW_Breaker_Bar
+ WULA_MW_Charge_Mace
+ WULA_MW_Lance
+ WULA_MW_ChainSword
+ WULA_MW_Glaive
+ WULA_RW_Fractal_AR
+ WULA_RW_StarDrift_SG
+ WULA_RW_Sphene_MG
+ WULA_RW_Handle_Cannon
+ WULA_RW_AutoCannon
+ WULA_RW_Auto_GL
+ WULA_RW_DM_AR
+ WULA_RW_DM_Cannon
+
+
+
+
+ 1.0
+ Legendary
+
+
+ 0.8
+ Masterwork
+
+
+ 0.5
+ Excellent
+
+
+ 0.2
+ Normal
+
+
+ 0.0
+ Poor
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.6/Defs/ThingDefs_Buildings/WULA_FallenEmpire_Buildings_Ritual.xml b/1.6/Defs/ThingDefs_Buildings/WULA_FallenEmpire_Buildings_Ritual.xml
new file mode 100644
index 00000000..cac34dca
--- /dev/null
+++ b/1.6/Defs/ThingDefs_Buildings/WULA_FallenEmpire_Buildings_Ritual.xml
@@ -0,0 +1,90 @@
+
+
+
+
+ WULA_OfferingPedestal
+
+ 一个用于进行灵能献祭的华丽基座。它可以作为灵能的冥想焦点,并能通过附近的灵能设施获得强化。它是启动帝国科技献祭仪式的关键建筑。
+ Normal
+ Standable
+ false
+
+ SupportPlantsOnly
+ false
+ false
+ false
+ false
+
+ Anomaly
+
+
+ 200
+
+ Things/Building/PsychicRitualSpot
+ Graphic_Single
+ (3, 3)
+
+ (3, 3)
+ Light
+
+ PsychicRituals
+
+
+ ITab_Entity
+
+ Misc
+ FloorEmplacement
+ true
+ false
+
+ 0
+ 0.08
+
+ false
+
+ PlaceWorker_NeverAdjacentUnstandableRadial
+
+ True
+
+
+ WULA_FallenEmpire_TechOffering
+ 10
+
+
+
+ ShardBeacon
+ VoidSculpture
+
+
+
+ MeditationFocusStrength
+
+ Void
+
+
+
+
+ ShardBeacon
+
+ 0.02
+ 9.9
+ 4
+ MeditationFocusPerBuilding
+ MeditationFocusPerBuildingAbstract
+
+
+
+ VoidSculpture
+
+ 0.02
+ 9.9
+ 6
+ MeditationFocusPerBuilding
+ MeditationFocusPerBuildingAbstract
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Ritual_Keys.xml b/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Ritual_Keys.xml
new file mode 100644
index 00000000..f418a32b
--- /dev/null
+++ b/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Ritual_Keys.xml
@@ -0,0 +1,10 @@
+
+
+
+ 献祭的回报
+ 你们的献祭得到了回应。虚空吐出了一件物品作为奖赏:{0} (品质: {1})。
+
+ 额外祭品
+ 在仪式中心附近放置的贵重物品提升了仪式的品质。这些物品将在仪式完成时被消耗。
+
+
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/PsychicRitual_TechOffering.cs b/Source/WulaFallenEmpire/PsychicRitual_TechOffering.cs
new file mode 100644
index 00000000..df372cef
--- /dev/null
+++ b/Source/WulaFallenEmpire/PsychicRitual_TechOffering.cs
@@ -0,0 +1,188 @@
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+using Verse.AI.Group;
+
+namespace WulaFallenEmpire
+{
+ // 用于在XML中定义祭品
+ public class OfferingItem
+ {
+ public ThingDef thingDef;
+ public float power;
+ }
+
+ public class QualityThreshold
+ {
+ public float threshold;
+ public QualityCategory quality;
+ }
+
+ public class PsychicRitual_TechOffering : PsychicRitualDef_InvocationCircle
+ {
+ // 从XML加载的额外祭品列表
+ public List extraOfferings = new List();
+
+ // 从XML加载的奖励池
+ public List rewardWeaponPool = new List();
+
+ // 从XML加载的品质阈值
+ public List qualityThresholds = new List();
+
+ // 重写计算最大能量的方法
+ public override void CalculateMaxPower(PsychicRitualRoleAssignments assignments, List powerFactorsOut, out float power)
+ {
+ // 首先调用基类方法
+ base.CalculateMaxPower(assignments, powerFactorsOut, out power);
+
+ IntVec3 center = assignments.Target.Cell;
+ Map map = assignments.Target.Map;
+ float offeringRadius = 8f;
+
+ float extraPowerFromOfferings = 0f;
+ if (!extraOfferings.NullOrEmpty())
+ {
+ var offeringThings = new Dictionary();
+ foreach(var offering in extraOfferings)
+ {
+ offeringThings[offering.thingDef] = offering.power;
+ }
+
+ foreach (Thing thing in GenRadial.RadialDistinctThingsAround(center, map, offeringRadius, useCenter: true))
+ {
+ if (offeringThings.TryGetValue(thing.def, out float value))
+ {
+ extraPowerFromOfferings += value * thing.stackCount;
+ }
+ }
+ }
+
+ if (extraPowerFromOfferings > 0)
+ {
+ powerFactorsOut?.Add(new QualityFactor
+ {
+ label = "WULA_ExtraOfferings".Translate(),
+ positive = true,
+ quality = extraPowerFromOfferings,
+ toolTip = "WULA_ExtraOfferings_Tooltip".Translate()
+ });
+ power += extraPowerFromOfferings;
+ }
+
+ power = UnityEngine.Mathf.Clamp01(power);
+ }
+
+ // 重写创建仪式步骤的方法
+ public override List CreateToils(PsychicRitual psychicRitual, PsychicRitualGraph parent)
+ {
+ // 获取基类的仪式步骤
+ List toils = base.CreateToils(psychicRitual, parent);
+
+ // 在最后添加我们自定义的奖励步骤
+ toils.Add(new PsychicRitualToil_TechOfferingOutcome(psychicRitual, this));
+
+ return toils;
+ }
+ }
+
+ // 自定义的仪式步骤,用于处理奖励
+ public class PsychicRitualToil_TechOfferingOutcome : PsychicRitualToil
+ {
+ private PsychicRitual psychicRitual;
+ private PsychicRitualDef def;
+
+ public PsychicRitualToil_TechOfferingOutcome(PsychicRitual psychicRitual, PsychicRitualDef def)
+ {
+ this.psychicRitual = psychicRitual;
+ this.def = def;
+ }
+
+ public override bool Tick(PsychicRitual psychicRitual, PsychicRitualGraph parent)
+ {
+ float power = psychicRitual.power;
+
+ // 消耗祭品
+ IntVec3 center = psychicRitual.assignments.Target.Cell;
+ Map map = psychicRitual.assignments.Target.Map;
+ float offeringRadius = 8f;
+
+ PsychicRitual_TechOffering ritualDef = (PsychicRitual_TechOffering)def;
+
+ if (!ritualDef.extraOfferings.NullOrEmpty())
+ {
+ var offeringThings = new Dictionary();
+ foreach(var offering in ritualDef.extraOfferings)
+ {
+ offeringThings[offering.thingDef] = offering.power;
+ }
+
+ foreach (Thing thing in GenRadial.RadialDistinctThingsAround(center, map, offeringRadius, useCenter: true))
+ {
+ if (offeringThings.ContainsKey(thing.def))
+ {
+ thing.Destroy(DestroyMode.Vanish);
+ }
+ }
+ }
+
+ // 从奖励池中随机选择一个武器
+ if (ritualDef.rewardWeaponPool.NullOrEmpty())
+ {
+ Log.Error($"[WulaFallenEmpire] Reward weapon pool is empty for {def.defName}");
+ return true;
+ }
+ ThingDef weaponDef = ritualDef.rewardWeaponPool.RandomElement();
+ if (weaponDef == null)
+ {
+ Log.Error($"[WulaFallenEmpire] Could not find weapon Def: {weaponDef.defName}");
+ return true;
+ }
+
+ // 根据能量值决定物品品质
+ QualityCategory quality = QualityCategory.Awful; // 默认最低品质
+ if (!ritualDef.qualityThresholds.NullOrEmpty())
+ {
+ // 对阈值列表按阈值从高到低排序
+ var sortedThresholds = ritualDef.qualityThresholds.OrderByDescending(t => t.threshold).ToList();
+ foreach (var threshold in sortedThresholds)
+ {
+ if (power >= threshold.threshold)
+ {
+ quality = threshold.quality;
+ break; // 找到第一个满足的阈值就跳出
+ }
+ }
+ }
+ else // 如果XML中没有定义,则使用硬编码的默认值
+ {
+ if (power >= 1.0f) { quality = QualityCategory.Legendary; }
+ else if (power >= 0.8f) { quality = QualityCategory.Masterwork; }
+ else if (power >= 0.5f) { quality = QualityCategory.Excellent; }
+ else if (power >= 0.2f) { quality = QualityCategory.Normal; }
+ else { quality = QualityCategory.Poor; }
+ }
+
+ // 创建物品并设置品质
+ Thing reward = ThingMaker.MakeThing(weaponDef);
+ CompQuality compQuality = reward.TryGetComp();
+ if (compQuality != null)
+ {
+ compQuality.SetQuality(quality, ArtGenerationContext.Colony);
+ }
+
+ // 在仪式中心点生成奖励物品
+ GenPlace.TryPlaceThing(reward, psychicRitual.assignments.Target.Cell, map, ThingPlaceMode.Near);
+
+ // 发送消息通知玩家
+ Find.LetterStack.ReceiveLetter(
+ "WULA_RitualReward_Label".Translate(),
+ "WULA_RitualReward_Description".Translate(reward.Label, quality.GetLabel()),
+ LetterDefOf.PositiveEvent,
+ new LookTargets(psychicRitual.assignments.Target.Cell, map)
+ );
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/Verb/ShootArc.cs b/Source/WulaFallenEmpire/Verb/ShootArc.cs
index 77633315..e31a1ef5 100644
--- a/Source/WulaFallenEmpire/Verb/ShootArc.cs
+++ b/Source/WulaFallenEmpire/Verb/ShootArc.cs
@@ -94,227 +94,123 @@ namespace WulaFallenEmpire
protected void MakeExplosion()
{
Pawn casterPawn = this.CasterPawn;
- bool spawned = casterPawn.Spawned;
- bool flag11 = spawned;
- if (flag11)
+ if (!casterPawn.Spawned || this.Props == null)
{
- Thing targetThing = this.currentTarget.Thing;
- bool flag = targetThing != null && this.IsTargetImmobile(this.currentTarget) && casterPawn.skills != null;
- bool flag12 = flag;
- if (flag12)
+ return;
+ }
+
+ // 技能学习逻辑 (只在目标是站立Pawn时)
+ if (this.currentTarget.Thing is Pawn targetPawn && !targetPawn.Downed && targetPawn.GetPosture() == PawnPosture.Standing && casterPawn.skills != null)
+ {
+ casterPawn.skills.Learn(SkillDefOf.Shooting, 250f * verbProps.AdjustedFullCycleTime(this, casterPawn), false, false);
+ }
+
+ float weaponDamageMultiplier = base.EquipmentSource.GetStatValue(StatDefOf.RangedWeapon_DamageMultiplier, true, -1);
+ int damageMultiplier = this.GetDamageAmount(weaponDamageMultiplier, null);
+ float armorPenetrationMultiplier = this.GetArmorPenetration(weaponDamageMultiplier, null);
+
+ // 总是先收集范围内的Pawn,为后续决策做准备
+ List cells = Verb_ShootArc.circularSectorCellsStartedCaster(casterPawn.Position, casterPawn.Map, this.currentTarget.Cell, this.Props.range, this.Props.affectedAngle, false).ToList();
+ HashSet hashSet = this.HashSetConverter(cells);
+ this.pawnConduct.Add(casterPawn);
+
+ foreach (IntVec3 cell in hashSet)
+ {
+ List list = casterPawn.Map.thingGrid.ThingsListAt(cell);
+ for (int num = list.Count - 1; num >= 0; num--)
{
- casterPawn.skills.Learn(SkillDefOf.Shooting, 250f * this.verbProps.AdjustedFullCycleTime(this, casterPawn), false, false);
- }
- bool flag2 = this.Props != null;
- bool flag13 = flag2;
- if (flag13)
- {
- List cells = Verb_ShootArc.circularSectorCellsStartedCaster(casterPawn.Position, casterPawn.Map, this.currentTarget.Cell, this.Props.range, this.Props.affectedAngle, false).ToList();
- HashSet hashSet = this.HashSetConverter(cells);
- this.pawnConduct.Add(casterPawn);
- float weaponDamageMultiplier = base.EquipmentSource.GetStatValue(StatDefOf.RangedWeapon_DamageMultiplier, true, -1);
- int damageMultiplier = this.GetDamageAmount(weaponDamageMultiplier, null);
- float armorPenetrationMultiplier = this.GetArmorPenetration(weaponDamageMultiplier, null);
- foreach (IntVec3 cell in hashSet)
+ if (list[num] is Pawn p)
{
- List list = casterPawn.Map.thingGrid.ThingsListAt(cell);
- for (int num = list.Count - 1; num >= 0; num--)
+ bool isFriendly = p.Faction != null && casterPawn.Faction != null && !p.Faction.HostileTo(casterPawn.Faction);
+ if (!this.Props.conductFriendly && isFriendly)
{
- if (list[num] is Pawn p)
- {
- // 新增友方过滤逻辑
- bool isFriendly = p.Faction != null
- && casterPawn.Faction != null
- && !p.Faction.HostileTo(casterPawn.Faction);
-
- if (!Props.conductFriendly && isFriendly)
- {
- continue; // 跳过友方目标
- }
-
- // 原有姿势检查
- bool isInvalidPosture = p.GetPosture() != PawnPosture.Standing
- && this.currentTarget.Thing != p;
-
- if (!isInvalidPosture)
- {
- this.pawnConduct.Add(p);
- }
- }
+ continue;
+ }
+ bool isInvalidPosture = p.GetPosture() != PawnPosture.Standing && this.currentTarget.Thing != p;
+ if (!isInvalidPosture)
+ {
+ this.pawnConduct.Add(p);
}
}
- bool isConductible = this.Props.isConductible;
- bool flag17 = isConductible;
- if (flag17)
- {
- for (int i = 0; i < this.Props.conductNum; i++)
- {
- bool flag6 = i > this.pawnConduct.Count - 2;
- bool flag18 = flag6;
- if (flag18)
- {
- break;
- }
- bool flag7 = this.Props.EMPDamageAmount > 0f;
- bool flag19 = flag7;
- if (flag19)
- {
- this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], DamageDefOf.EMP, this.Props.EMPDamageAmount, -1f);
- }
- this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], this.Props.damageDef, (float)damageMultiplier, armorPenetrationMultiplier);
- bool flag8 = this.verbProps.beamMoteDef != null;
- bool flag20 = flag8;
- if (flag20)
- {
- MoteMaker.MakeInteractionOverlay(this.verbProps.beamMoteDef, new TargetInfo(this.pawnConduct[i].Position, this.caster.Map, false), new TargetInfo(this.pawnConduct[i + 1].Position, this.caster.Map, false));
- }
- }
- }
- else
- {
- IntVec3 position = this.caster.Position;
- float num2 = Mathf.Atan2(-(float)(this.currentTarget.Cell.z - position.z), (float)(this.currentTarget.Cell.x - position.x)) * 57.29578f;
- bool flag9 = num2 - this.Props.affectedAngle < -180f || num2 + this.Props.affectedAngle > 180f;
- bool flag21 = flag9;
- if (flag21)
- {
- FloatRange? affectedAngle = new FloatRange?(new FloatRange(Verb_ShootArc.AngleWrapped(num2 - this.Props.affectedAngle), 180f));
- // 修正后的爆炸调用(参数通过命名对齐)
- GenExplosion.DoExplosion(
- center: casterPawn.Position,
- map: this.caster.MapHeld,
- radius: this.verbProps.range,
- damType: this.Props.damageDef,
- instigator: null,
- damAmount: damageMultiplier,
- armorPenetration: armorPenetrationMultiplier,
- explosionSound: null,
- weapon: this.CasterPawn.equipment.Primary.def,
- projectile: null,
- intendedTarget: null,
- postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
- postExplosionSpawnChance: 0f,
- postExplosionSpawnThingCount: 1,
- postExplosionGasType: null,
- postExplosionGasRadiusOverride: null,
- postExplosionGasAmount: 0,
- applyDamageToExplosionCellsNeighbors: false,
- preExplosionSpawnThingDef: null,
- preExplosionSpawnChance: 0f,
- preExplosionSpawnThingCount: 1,
- chanceToStartFire: 0f,
- damageFalloff: false,
- direction: null,
- ignoredThings: null,
- affectedAngle: affectedAngle,
- doVisualEffects: true,
- propagationSpeed: 0.6f,
- excludeRadius: 0f,
- doSoundEffects: false,
- postExplosionSpawnThingDefWater: null,
- screenShakeFactor: 1f,
- flammabilityChanceCurve: null,
- overrideCells: null,
- postExplosionSpawnSingleThingDef: null,
- preExplosionSpawnSingleThingDef: null
- );
-
- affectedAngle = new FloatRange?(new FloatRange(-180f, Verb_ShootArc.AngleWrapped(num2 + this.Props.affectedAngle)));
- // 第二次爆炸调用(参数结构相同)
- GenExplosion.DoExplosion(
- center: casterPawn.Position,
- map: this.caster.MapHeld,
- radius: this.verbProps.range,
- damType: this.Props.damageDef,
- instigator: null,
- damAmount: damageMultiplier,
- armorPenetration: armorPenetrationMultiplier,
- explosionSound: null,
- weapon: this.CasterPawn.equipment.Primary.def,
- projectile: null,
- intendedTarget: null,
- postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
- postExplosionSpawnChance: 0f,
- postExplosionSpawnThingCount: 1,
- postExplosionGasType: null,
- postExplosionGasRadiusOverride: null,
- postExplosionGasAmount: 0,
- applyDamageToExplosionCellsNeighbors: false,
- preExplosionSpawnThingDef: null,
- preExplosionSpawnChance: 0f,
- preExplosionSpawnThingCount: 1,
- chanceToStartFire: 0f,
- damageFalloff: false,
- direction: null,
- ignoredThings: null,
- affectedAngle: affectedAngle,
- doVisualEffects: true,
- propagationSpeed: 0.6f,
- excludeRadius: 0f,
- doSoundEffects: false,
- postExplosionSpawnThingDefWater: null,
- screenShakeFactor: 1f,
- flammabilityChanceCurve: null,
- overrideCells: null,
- postExplosionSpawnSingleThingDef: null,
- preExplosionSpawnSingleThingDef: null
- );
- }
- else
- {
- FloatRange? affectedAngle2 = new FloatRange?(new FloatRange(num2 - this.Props.affectedAngle, num2 + this.Props.affectedAngle));
- GenExplosion.DoExplosion(
- center: casterPawn.Position,
- map: this.caster.MapHeld,
- radius: this.verbProps.range,
- damType: this.Props.damageDef,
- instigator: null,
- damAmount: damageMultiplier,
- armorPenetration: armorPenetrationMultiplier,
- explosionSound: null,
- weapon: this.CasterPawn.equipment.Primary.def,
- projectile: null,
- intendedTarget: null,
- postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
- postExplosionSpawnChance: 0f,
- postExplosionSpawnThingCount: 1,
- postExplosionGasType: null,
- postExplosionGasRadiusOverride: null,
- postExplosionGasAmount: 0,
- applyDamageToExplosionCellsNeighbors: false,
- preExplosionSpawnThingDef: null,
- preExplosionSpawnChance: 0f,
- preExplosionSpawnThingCount: 1,
- chanceToStartFire: 0f,
- damageFalloff: false,
- direction: null,
- ignoredThings: null,
- affectedAngle: affectedAngle2,
- doVisualEffects: true,
- propagationSpeed: 0.6f,
- excludeRadius: 0f,
- doSoundEffects: false,
- postExplosionSpawnThingDefWater: null,
- screenShakeFactor: 1f,
- flammabilityChanceCurve: null,
- overrideCells: null,
- postExplosionSpawnSingleThingDef: null,
- preExplosionSpawnSingleThingDef: null
- );
- }
- for (int j = 1; j < this.pawnConduct.Count(); j++)
- {
- bool flag10 = this.Props.EMPDamageAmount > 0f;
- bool flag22 = flag10;
- if (flag22)
- {
- this.TargetTakeDamage(casterPawn, this.pawnConduct[j], DamageDefOf.EMP, this.Props.EMPDamageAmount, -1f);
- }
- }
- }
- this.pawnConduct.Clear();
}
}
+
+ // 决策:如果设为导电模式且有至少一个传导目标,则进行链式攻击
+ if (this.Props.isConductible && this.pawnConduct.Count > 1)
+ {
+ for (int i = 0; i < this.Props.conductNum && i < this.pawnConduct.Count - 1; i++)
+ {
+ if (this.Props.EMPDamageAmount > 0f)
+ {
+ this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], DamageDefOf.EMP, this.Props.EMPDamageAmount, -1f);
+ }
+ this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], this.Props.damageDef, (float)damageMultiplier, armorPenetrationMultiplier);
+ if (this.verbProps.beamMoteDef != null)
+ {
+ MoteMaker.MakeInteractionOverlay(this.verbProps.beamMoteDef, new TargetInfo(this.pawnConduct[i].Position, this.caster.Map, false), new TargetInfo(this.pawnConduct[i + 1].Position, this.caster.Map, false));
+ }
+ }
+ }
+ // 否则(非导电模式,或没有传导目标),执行一次普通的单体攻击
+ else
+ {
+ Thing primaryTarget = this.currentTarget.Thing;
+ if (primaryTarget != null)
+ {
+ float angle = (primaryTarget.Position - this.caster.Position).AngleFlat;
+ DamageInfo dinfo = new DamageInfo(this.Props.damageDef, (float)damageMultiplier, armorPenetrationMultiplier, angle, this.caster, null, base.EquipmentSource.def, DamageInfo.SourceCategory.ThingOrUnknown, this.currentTarget.Thing);
+ primaryTarget.TakeDamage(dinfo);
+ }
+
+ // 无论是否命中,都显示视觉效果
+ if (this.verbProps.beamMoteDef != null)
+ {
+ MoteMaker.MakeInteractionOverlay(this.verbProps.beamMoteDef, new TargetInfo(this.caster.Position, this.caster.Map, false), new TargetInfo(this.currentTarget.Cell, this.caster.Map, false));
+ }
+ }
+ this.pawnConduct.Clear();
+ }
+
+ private void DoExplosion(Pawn casterPawn, int damAmount, float armorPenetration, FloatRange? affectedAngle)
+ {
+ GenExplosion.DoExplosion(
+ center: casterPawn.Position,
+ map: this.caster.MapHeld,
+ radius: this.verbProps.range,
+ damType: this.Props.damageDef,
+ instigator: casterPawn, // Corrected
+ damAmount: damAmount,
+ armorPenetration: armorPenetration,
+ explosionSound: null,
+ weapon: this.CasterPawn.equipment?.Primary?.def, // Safety check
+ projectile: null,
+ intendedTarget: this.currentTarget.Thing, // Corrected
+ postExplosionSpawnThingDef: null, // Simplified
+ postExplosionSpawnChance: 0f,
+ postExplosionSpawnThingCount: 1,
+ postExplosionGasType: null,
+ postExplosionGasRadiusOverride: null,
+ postExplosionGasAmount: 0,
+ applyDamageToExplosionCellsNeighbors: false,
+ preExplosionSpawnThingDef: null,
+ preExplosionSpawnChance: 0f,
+ preExplosionSpawnThingCount: 1,
+ chanceToStartFire: 0f,
+ damageFalloff: false,
+ direction: null,
+ ignoredThings: null,
+ affectedAngle: affectedAngle,
+ doVisualEffects: true,
+ propagationSpeed: 0.6f,
+ excludeRadius: 0f,
+ doSoundEffects: false,
+ postExplosionSpawnThingDefWater: null,
+ screenShakeFactor: 1f,
+ flammabilityChanceCurve: null,
+ overrideCells: null,
+ postExplosionSpawnSingleThingDef: null,
+ preExplosionSpawnSingleThingDef: null
+ );
}
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index 589cf25e..1a6de663 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -118,6 +118,7 @@
+
diff --git a/美术与文本源文件/故障的arc代码之扇形攻击.txt b/美术与文本源文件/故障的arc代码之扇形攻击.txt
new file mode 100644
index 00000000..e837ec66
--- /dev/null
+++ b/美术与文本源文件/故障的arc代码之扇形攻击.txt
@@ -0,0 +1,486 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using UnityEngine;
+using Verse;
+using Verse.Sound;
+
+namespace WulaFallenEmpire
+{
+ public class VerbProperties_Arc : VerbProperties
+ {
+ public DamageDef damageDef;
+
+ public float EMPDamageAmount = -1f;
+
+ public int damageAmount = -1;
+
+ public float armorPenetration = -1f;
+
+ public float affectedAngle;
+
+ public bool isConductible = false;
+
+ public int conductNum;
+
+ public bool conductFriendly = false;
+ }
+
+ public class Verb_ShootArc : Verb
+ {
+ private VerbProperties_Arc Props
+ {
+ get
+ {
+ return (VerbProperties_Arc)this.verbProps;
+ }
+ }
+
+ private int damageAmount
+ {
+ get
+ {
+ return (this.Props.damageAmount > 0) ? this.Props.damageAmount : this.verbProps.beamDamageDef.defaultDamage;
+ }
+ }
+
+ private float armorPenetration
+ {
+ get
+ {
+ return (this.Props.armorPenetration > 0f) ? this.Props.armorPenetration : this.verbProps.beamDamageDef.defaultArmorPenetration;
+ }
+ }
+
+ public override void WarmupComplete()
+ {
+ this.TryCastShot();
+ }
+
+ protected override bool TryCastShot()
+ {
+ this.MakeExplosion();
+ bool flag = this.verbProps.soundCast != null;
+ bool flag3 = flag;
+ if (flag3)
+ {
+ this.verbProps.soundCast.PlayOneShot(new TargetInfo(this.caster.Position, this.caster.MapHeld, false));
+ }
+ bool flag2 = this.verbProps.soundCastTail != null;
+ bool flag4 = flag2;
+ if (flag4)
+ {
+ this.verbProps.soundCastTail.PlayOneShotOnCamera(this.caster.Map);
+ }
+ return true;
+ }
+
+ private bool IsTargetImmobile(LocalTargetInfo target)
+ {
+ Thing thing = target.Thing;
+ Pawn pawn = thing as Pawn;
+ return pawn != null && !pawn.Downed && pawn.GetPosture() == PawnPosture.Standing;
+ }
+
+ public override bool CanHitTarget(LocalTargetInfo targ)
+ {
+ bool flag = this.caster == null || !this.caster.Spawned;
+ bool flag2 = flag;
+ return !flag2 && (targ == this.caster || this.CanHitTargetFrom(this.caster.Position, targ));
+ }
+
+ protected void MakeExplosion()
+ {
+ Pawn casterPawn = this.CasterPawn;
+ bool spawned = casterPawn.Spawned;
+ bool flag11 = spawned;
+ if (flag11)
+ {
+ Thing targetThing = this.currentTarget.Thing;
+ bool flag = targetThing != null && this.IsTargetImmobile(this.currentTarget) && casterPawn.skills != null;
+ bool flag12 = flag;
+ if (flag12)
+ {
+ casterPawn.skills.Learn(SkillDefOf.Shooting, 250f * this.verbProps.AdjustedFullCycleTime(this, casterPawn), false, false);
+ }
+ bool flag2 = this.Props != null;
+ bool flag13 = flag2;
+ if (flag13)
+ {
+ List cells = Verb_ShootArc.circularSectorCellsStartedCaster(casterPawn.Position, casterPawn.Map, this.currentTarget.Cell, this.Props.range, this.Props.affectedAngle, false).ToList();
+ HashSet hashSet = this.HashSetConverter(cells);
+ this.pawnConduct.Add(casterPawn);
+ float weaponDamageMultiplier = base.EquipmentSource.GetStatValue(StatDefOf.RangedWeapon_DamageMultiplier, true, -1);
+ int damageMultiplier = this.GetDamageAmount(weaponDamageMultiplier, null);
+ float armorPenetrationMultiplier = this.GetArmorPenetration(weaponDamageMultiplier, null);
+ foreach (IntVec3 cell in hashSet)
+ {
+ List list = casterPawn.Map.thingGrid.ThingsListAt(cell);
+ for (int num = list.Count - 1; num >= 0; num--)
+ {
+ if (list[num] is Pawn p)
+ {
+ // 新增友方过滤逻辑
+ bool isFriendly = p.Faction != null
+ && casterPawn.Faction != null
+ && !p.Faction.HostileTo(casterPawn.Faction);
+
+ if (!Props.conductFriendly && isFriendly)
+ {
+ continue; // 跳过友方目标
+ }
+
+ // 原有姿势检查
+ bool isInvalidPosture = p.GetPosture() != PawnPosture.Standing
+ && this.currentTarget.Thing != p;
+
+ if (!isInvalidPosture)
+ {
+ this.pawnConduct.Add(p);
+ }
+ }
+ }
+ }
+ bool isConductible = this.Props.isConductible;
+ bool flag17 = isConductible;
+ if (flag17)
+ {
+ for (int i = 0; i < this.Props.conductNum; i++)
+ {
+ bool flag6 = i > this.pawnConduct.Count - 2;
+ bool flag18 = flag6;
+ if (flag18)
+ {
+ break;
+ }
+ bool flag7 = this.Props.EMPDamageAmount > 0f;
+ bool flag19 = flag7;
+ if (flag19)
+ {
+ this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], DamageDefOf.EMP, this.Props.EMPDamageAmount, -1f);
+ }
+ this.TargetTakeDamage(casterPawn, this.pawnConduct[i + 1], this.Props.damageDef, (float)damageMultiplier, armorPenetrationMultiplier);
+ bool flag8 = this.verbProps.beamMoteDef != null;
+ bool flag20 = flag8;
+ if (flag20)
+ {
+ MoteMaker.MakeInteractionOverlay(this.verbProps.beamMoteDef, new TargetInfo(this.pawnConduct[i].Position, this.caster.Map, false), new TargetInfo(this.pawnConduct[i + 1].Position, this.caster.Map, false));
+ }
+ }
+ }
+ else
+ {
+ IntVec3 position = this.caster.Position;
+ float num2 = Mathf.Atan2(-(float)(this.currentTarget.Cell.z - position.z), (float)(this.currentTarget.Cell.x - position.x)) * 57.29578f;
+ bool flag9 = num2 - this.Props.affectedAngle < -180f || num2 + this.Props.affectedAngle > 180f;
+ bool flag21 = flag9;
+ if (flag21)
+ {
+ FloatRange? affectedAngle = new FloatRange?(new FloatRange(Verb_ShootArc.AngleWrapped(num2 - this.Props.affectedAngle), 180f));
+ // 修正后的爆炸调用(参数通过命名对齐)
+ GenExplosion.DoExplosion(
+ center: casterPawn.Position,
+ map: this.caster.MapHeld,
+ radius: this.verbProps.range,
+ damType: this.Props.damageDef,
+ instigator: null,
+ damAmount: damageMultiplier,
+ armorPenetration: armorPenetrationMultiplier,
+ explosionSound: null,
+ weapon: this.CasterPawn.equipment.Primary.def,
+ projectile: null,
+ intendedTarget: null,
+ postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
+ postExplosionSpawnChance: 0f,
+ postExplosionSpawnThingCount: 1,
+ postExplosionGasType: null,
+ postExplosionGasRadiusOverride: null,
+ postExplosionGasAmount: 0,
+ applyDamageToExplosionCellsNeighbors: false,
+ preExplosionSpawnThingDef: null,
+ preExplosionSpawnChance: 0f,
+ preExplosionSpawnThingCount: 1,
+ chanceToStartFire: 0f,
+ damageFalloff: false,
+ direction: null,
+ ignoredThings: null,
+ affectedAngle: affectedAngle,
+ doVisualEffects: true,
+ propagationSpeed: 0.6f,
+ excludeRadius: 0f,
+ doSoundEffects: false,
+ postExplosionSpawnThingDefWater: null,
+ screenShakeFactor: 1f,
+ flammabilityChanceCurve: null,
+ overrideCells: null,
+ postExplosionSpawnSingleThingDef: null,
+ preExplosionSpawnSingleThingDef: null
+ );
+
+ affectedAngle = new FloatRange?(new FloatRange(-180f, Verb_ShootArc.AngleWrapped(num2 + this.Props.affectedAngle)));
+ // 第二次爆炸调用(参数结构相同)
+ GenExplosion.DoExplosion(
+ center: casterPawn.Position,
+ map: this.caster.MapHeld,
+ radius: this.verbProps.range,
+ damType: this.Props.damageDef,
+ instigator: null,
+ damAmount: damageMultiplier,
+ armorPenetration: armorPenetrationMultiplier,
+ explosionSound: null,
+ weapon: this.CasterPawn.equipment.Primary.def,
+ projectile: null,
+ intendedTarget: null,
+ postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
+ postExplosionSpawnChance: 0f,
+ postExplosionSpawnThingCount: 1,
+ postExplosionGasType: null,
+ postExplosionGasRadiusOverride: null,
+ postExplosionGasAmount: 0,
+ applyDamageToExplosionCellsNeighbors: false,
+ preExplosionSpawnThingDef: null,
+ preExplosionSpawnChance: 0f,
+ preExplosionSpawnThingCount: 1,
+ chanceToStartFire: 0f,
+ damageFalloff: false,
+ direction: null,
+ ignoredThings: null,
+ affectedAngle: affectedAngle,
+ doVisualEffects: true,
+ propagationSpeed: 0.6f,
+ excludeRadius: 0f,
+ doSoundEffects: false,
+ postExplosionSpawnThingDefWater: null,
+ screenShakeFactor: 1f,
+ flammabilityChanceCurve: null,
+ overrideCells: null,
+ postExplosionSpawnSingleThingDef: null,
+ preExplosionSpawnSingleThingDef: null
+ );
+ }
+ else
+ {
+ FloatRange? affectedAngle2 = new FloatRange?(new FloatRange(num2 - this.Props.affectedAngle, num2 + this.Props.affectedAngle));
+ GenExplosion.DoExplosion(
+ center: casterPawn.Position,
+ map: this.caster.MapHeld,
+ radius: this.verbProps.range,
+ damType: this.Props.damageDef,
+ instigator: null,
+ damAmount: damageMultiplier,
+ armorPenetration: armorPenetrationMultiplier,
+ explosionSound: null,
+ weapon: this.CasterPawn.equipment.Primary.def,
+ projectile: null,
+ intendedTarget: null,
+ postExplosionSpawnThingDef: ThingDefOf.Filth_FlammableBile,
+ postExplosionSpawnChance: 0f,
+ postExplosionSpawnThingCount: 1,
+ postExplosionGasType: null,
+ postExplosionGasRadiusOverride: null,
+ postExplosionGasAmount: 0,
+ applyDamageToExplosionCellsNeighbors: false,
+ preExplosionSpawnThingDef: null,
+ preExplosionSpawnChance: 0f,
+ preExplosionSpawnThingCount: 1,
+ chanceToStartFire: 0f,
+ damageFalloff: false,
+ direction: null,
+ ignoredThings: null,
+ affectedAngle: affectedAngle2,
+ doVisualEffects: true,
+ propagationSpeed: 0.6f,
+ excludeRadius: 0f,
+ doSoundEffects: false,
+ postExplosionSpawnThingDefWater: null,
+ screenShakeFactor: 1f,
+ flammabilityChanceCurve: null,
+ overrideCells: null,
+ postExplosionSpawnSingleThingDef: null,
+ preExplosionSpawnSingleThingDef: null
+ );
+ }
+ for (int j = 1; j < this.pawnConduct.Count(); j++)
+ {
+ bool flag10 = this.Props.EMPDamageAmount > 0f;
+ bool flag22 = flag10;
+ if (flag22)
+ {
+ this.TargetTakeDamage(casterPawn, this.pawnConduct[j], DamageDefOf.EMP, this.Props.EMPDamageAmount, -1f);
+ }
+ }
+ }
+ this.pawnConduct.Clear();
+ }
+ }
+ }
+
+
+ public override void DrawHighlight(LocalTargetInfo target)
+ {
+ base.DrawHighlight(target);
+ bool isValid = target.IsValid;
+ bool flag = isValid;
+ if (flag)
+ {
+ IntVec3 position = this.caster.Position;
+ float num = Mathf.Atan2(-(float)(target.Cell.z - position.z), (float)(target.Cell.x - position.x)) * 57.29578f;
+ Verb_ShootArc.RenderPredictedAreaOfEffect(this.caster.Position, this.Props.range, this.verbProps.explosionRadiusRingColor, new FloatRange(num - this.Props.affectedAngle, num + this.Props.affectedAngle));
+ }
+ }
+
+ public static void RenderPredictedAreaOfEffect(IntVec3 loc, float radius, Color color, FloatRange affectedAngle)
+ {
+ bool flag = affectedAngle.min < -180f || affectedAngle.max > 180f;
+ bool flag2 = flag;
+ List cellsSum;
+ if (flag2)
+ {
+ DamageWorker worker = DamageDefOf.Bomb.Worker;
+ Map currentMap = Find.CurrentMap;
+ FloatRange? affectedAngle2 = new FloatRange?(new FloatRange(Verb_ShootArc.AngleWrapped(affectedAngle.min), 180f));
+ List cells = worker.ExplosionCellsToHit(loc, currentMap, radius, null, null, affectedAngle2).ToList();
+ DamageWorker worker2 = DamageDefOf.Bomb.Worker;
+ Map currentMap2 = Find.CurrentMap;
+ affectedAngle2 = new FloatRange?(new FloatRange(-180f, Verb_ShootArc.AngleWrapped(affectedAngle.max)));
+ List cells2 = worker2.ExplosionCellsToHit(loc, currentMap2, radius, null, null, affectedAngle2).ToList();
+ cellsSum = cells.Concat(cells2).ToList();
+ }
+ else
+ {
+ DamageWorker worker3 = DamageDefOf.Bomb.Worker;
+ Map currentMap3 = Find.CurrentMap;
+ FloatRange? affectedAngle3 = new FloatRange?(affectedAngle);
+ cellsSum = worker3.ExplosionCellsToHit(loc, currentMap3, radius, null, null, affectedAngle3).ToList();
+ }
+ GenDraw.DrawFieldEdges(cellsSum, color, null);
+ }
+
+ public static float AngleWrapped(float angle)
+ {
+ while (angle > 180f)
+ {
+ angle -= 360f;
+ }
+ while (angle < -180f)
+ {
+ angle += 360f;
+ }
+ return (angle == 180f) ? -180f : angle;
+ }
+
+ private static IEnumerable circularSectorCellsStartedCaster(IntVec3 center, Map map, IntVec3 target, float radius, float angle, bool useCenter = false)
+ {
+ float num = Mathf.Atan2(-(float)(target.z - center.z), (float)(target.x - center.x)) * 57.29578f;
+ FloatRange affectedAngle = new FloatRange(num - angle, num + angle);
+ bool flag = affectedAngle.min < -180f || affectedAngle.max > 180f;
+ bool flag2 = flag;
+ List cellsSum;
+ if (flag2)
+ {
+ DamageWorker worker = DamageDefOf.Bomb.Worker;
+ FloatRange? affectedAngle2 = new FloatRange?(new FloatRange(Verb_ShootArc.AngleWrapped(affectedAngle.min), 180f));
+ List cells = worker.ExplosionCellsToHit(center, map, radius, null, null, affectedAngle2).ToList();
+ DamageWorker worker2 = DamageDefOf.Bomb.Worker;
+ affectedAngle2 = new FloatRange?(new FloatRange(-180f, Verb_ShootArc.AngleWrapped(affectedAngle.max)));
+ List cells2 = worker2.ExplosionCellsToHit(center, map, radius, null, null, affectedAngle2).ToList();
+ cellsSum = cells.Concat(cells2).ToList();
+ }
+ else
+ {
+ DamageWorker worker3 = DamageDefOf.Bomb.Worker;
+ FloatRange? affectedAngle3 = new FloatRange?(affectedAngle);
+ cellsSum = worker3.ExplosionCellsToHit(center, map, radius, null, null, affectedAngle3).ToList();
+ }
+ return cellsSum;
+ }
+
+ protected virtual HashSet HashSetConverter(IEnumerable points)
+ {
+ HashSet hashSet = new HashSet();
+ bool flag = points.Any();
+ bool flag2 = flag;
+ if (flag2)
+ {
+ foreach (IntVec3 point in points)
+ {
+ hashSet.Add(point);
+ }
+ }
+ return hashSet;
+ }
+
+ private void TargetTakeDamage(Pawn caster, Pawn target, DamageDef damageDef, float damageAmount, float armorPenetration = -1f)
+ {
+ bool flag = caster == null || target == null;
+ bool flag2 = flag;
+ if (flag2)
+ {
+ Log.Error("TargetTakeDamage has null caster or target");
+ }
+ else
+ {
+ float angleFlat = (this.currentTarget.Cell - caster.Position).AngleFlat;
+ BattleLogEntry_RangedImpact log = new BattleLogEntry_RangedImpact(caster, target, this.currentTarget.Thing, base.EquipmentSource.def, null, null);
+ DamageInfo dinfo = new DamageInfo(damageDef, damageAmount, armorPenetration, angleFlat, caster, null, base.EquipmentSource.def, DamageInfo.SourceCategory.ThingOrUnknown, this.currentTarget.Thing, true, true, QualityCategory.Normal, true);
+ target.TakeDamage(dinfo).AssociateWithLog(log);
+ }
+ }
+
+ public int GetDamageAmount(float weaponDamageMultiplier, StringBuilder explanation = null)
+ {
+ int num = this.damageAmount;
+ bool flag = explanation != null;
+ bool flag3 = flag;
+ if (flag3)
+ {
+ explanation.AppendLine("StatsReport_BaseValue".Translate() + ": " + num.ToString());
+ explanation.Append("StatsReport_QualityMultiplier".Translate() + ": " + weaponDamageMultiplier.ToStringPercent());
+ }
+ num = Mathf.RoundToInt((float)num * weaponDamageMultiplier);
+ bool flag2 = explanation != null;
+ bool flag4 = flag2;
+ if (flag4)
+ {
+ explanation.AppendLine();
+ explanation.AppendLine();
+ explanation.Append("StatsReport_FinalValue".Translate() + ": " + num.ToString());
+ }
+ return num;
+ }
+
+ public float GetArmorPenetration(float weaponDamageMultiplier, StringBuilder explanation = null)
+ {
+ float num = this.armorPenetration;
+ bool flag = num < 0f;
+ bool flag4 = flag;
+ if (flag4)
+ {
+ num = (float)this.damageAmount * 0.015f;
+ }
+ bool flag2 = explanation != null;
+ bool flag5 = flag2;
+ if (flag5)
+ {
+ explanation.AppendLine("StatsReport_BaseValue".Translate() + ": " + num.ToStringPercent());
+ explanation.AppendLine();
+ explanation.Append("StatsReport_QualityMultiplier".Translate() + ": " + weaponDamageMultiplier.ToStringPercent());
+ }
+ num *= weaponDamageMultiplier;
+ bool flag3 = explanation != null;
+ bool flag6 = flag3;
+ if (flag6)
+ {
+ explanation.AppendLine();
+ explanation.AppendLine();
+ explanation.Append("StatsReport_FinalValue".Translate() + ": " + num.ToStringPercent());
+ }
+ return num;
+ }
+
+ public List pawnConduct = new List();
+ }
+}
+