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