diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 426b9e4..59b7d73 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml index 1b2d358..b9dc14f 100644 --- a/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml +++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities.xml @@ -22,16 +22,46 @@ -
  • - ARA_Proj_StrongSludgeSpray - 18 - AcidSpray_Directional -
  • ARA_Proj_EggSac
  • + + + ARA_AcidSprayBurst + + 快速连续喷射多次腐蚀性酸液,覆盖一片区域。 + UI/Abilities/AcidSpray + 5000 + true + 300 + true + false + AcidSpray_Warmup + + Verb_CastAbility + 14.9 + 0.25 + AcidSpray_Resolve + + true + + + +
  • + + ARA_Proj_StrongSludgeSpray + 18 + AcidSpray_Directional + + + 32 + 12 +
  • +
    +
    + ARA_Proj_StrongSludgeSpray diff --git a/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml b/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml new file mode 100644 index 0000000..0456014 --- /dev/null +++ b/1.6/1.6/Defs/DamageDefs/ARA_Damages.xml @@ -0,0 +1,17 @@ + + + + ARA_AcidBurn + + +
  • + ARA_AcidCoverd + 0.01 +
  • +
    + DamageWorker_AddInjury + Sharp + AcidBurn + false +
    +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/HediffDefs/ARA_Hediffs.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs.xml new file mode 100644 index 0000000..e7e4cdb --- /dev/null +++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs.xml @@ -0,0 +1,89 @@ + + + + + ARA_AcidCoverd + + 你的身上沾上了虫族强酸,可能会痛一会。 + (1, 1, 0.8) + ArachnaeSwarm.HediffCurseFlame + +
  • + -4 +
  • +
  • + 1800 +
  • +
  • + + +
  • + ARA_AcidBurn + 1~5 + 40 +
  • + + +
  • + + true +
  • +
  • + + 0.2 + + 1.25 + + + -0.15 + -0.1 + +
  • +
  • + + 0.35 + + 1.75 + + + -0.35 + -0.3 + +
  • +
  • + + 0.5 + + 2.35 + + + -0.65 + -0.6 + +
  • +
  • + + 0.65 + + 2.85 + + + -0.8 + -0.75 + +
  • +
  • + + 0.85 + + 3.25 + + + -2 + -2 + +
  • +
    +
    + +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml index bb11623..bafb722 100644 --- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml +++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml @@ -16,6 +16,7 @@ false
  • ARA_EggSpew
  • +
  • ARA_AcidSprayBurst
  • diff --git a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml index d9ba36b..efd5b1b 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_InteractiveEggSac.xml @@ -7,9 +7,11 @@ 一个黏滑的囊状物,可以通过交互来孵化特定的昆虫。 Building Building + (1,1) Things/Building/EggSac Graphic_Random + (1.5,1.5) Building PassThroughOnly @@ -18,7 +20,7 @@ Normal Light - 20 + 200 0.4 -6 diff --git a/Source/ARA.code-workspace b/Source/ARA.code-workspace new file mode 100644 index 0000000..40c75ee --- /dev/null +++ b/Source/ARA.code-workspace @@ -0,0 +1,13 @@ +{ + "folders": [ + { + "name": "ArachnaeSwarm", + "path": ".." + }, + { + "name": "Data", + "path": "../../../Data" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 943fcf2..1ee6c58 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -71,6 +71,9 @@ + + + diff --git a/Source/ArachnaeSwarm/CompAbilityEffect_SprayLiquidMulti.cs b/Source/ArachnaeSwarm/CompAbilityEffect_SprayLiquidMulti.cs new file mode 100644 index 0000000..b20154f --- /dev/null +++ b/Source/ArachnaeSwarm/CompAbilityEffect_SprayLiquidMulti.cs @@ -0,0 +1,177 @@ +using System.Collections.Generic; +using RimWorld; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompAbilityEffect_SprayLiquidMulti : CompAbilityEffect + { + private enum State + { + Ready, + Shooting + } + + private State state = State.Ready; + private int ticksUntilNextShot; + private int shotsLeft; + private List currentAffectedCells; + + private new CompProperties_AbilitySprayLiquidMulti Props => (CompProperties_AbilitySprayLiquidMulti)props; + private Pawn Pawn => parent.pawn; + + public override void Apply(LocalTargetInfo target, LocalTargetInfo dest) + { + base.Apply(target, dest); + + if (state == State.Ready) + { + currentAffectedCells = AffectedCells(target); + if (currentAffectedCells.Count > 0) + { + Fire(currentAffectedCells); + + shotsLeft = Props.shotCount - 1; + if (shotsLeft > 0) + { + state = State.Shooting; + ticksUntilNextShot = Props.ticksBetweenShots; + } + } + } + } + + public override void CompTick() + { + base.CompTick(); + + if (state == State.Shooting) + { + if (ticksUntilNextShot > 0) + { + ticksUntilNextShot--; + } + else + { + if (shotsLeft > 0) + { + Fire(currentAffectedCells); + shotsLeft--; + ticksUntilNextShot = Props.ticksBetweenShots; + } + + if (shotsLeft <= 0) + { + state = State.Ready; + currentAffectedCells = null; + } + } + } + } + + private void Fire(List cells) + { + if (Pawn == null || !Pawn.Spawned || Props.projectileDef == null || cells == null) + { + state = State.Ready; + return; + } + + if (Props.sprayEffecter != null) + { + Effecter eff = Props.sprayEffecter.Spawn(Pawn.Position, Pawn.Map); + eff.Cleanup(); + } + + foreach (var cell in cells) + { + if (Pawn.Position.IsValid && cell.IsValid) + { + Projectile projectile = (Projectile)GenSpawn.Spawn(Props.projectileDef, Pawn.Position, Pawn.Map); + projectile.Launch(Pawn, Pawn.DrawPos, cell, cell, ProjectileHitFlags.IntendedTarget); + } + } + } + + public override void DrawEffectPreview(LocalTargetInfo target) + { + GenDraw.DrawFieldEdges(AffectedCells(target)); + } + + public override bool AICanTargetNow(LocalTargetInfo target) + { + if (Pawn.Faction != null) + { + foreach (IntVec3 item in AffectedCells(target)) + { + List thingList = item.GetThingList(Pawn.Map); + for (int i = 0; i < thingList.Count; i++) + { + if (thingList[i].Faction == Pawn.Faction) + { + return false; + } + } + } + } + return true; + } + + private List AffectedCells(LocalTargetInfo target) + { + List> tmpCellDots = new List>(); + List tmpCells = new List(); + + tmpCellDots.Clear(); + tmpCells.Clear(); + tmpCellDots.Add(new Pair(target.Cell, 999f)); + + if (Props.numCellsToHit > 1) + { + Vector3 vector = Pawn.Position.ToVector3Shifted().Yto0(); + Vector3 vector2 = target.Cell.ToVector3Shifted().Yto0(); + IntVec3[] array; + int num; + if (Props.numCellsToHit < 10) + { + array = GenAdj.AdjacentCells; + num = 8; + } + else + { + array = GenRadial.RadialPattern; + num = Props.numCellsToHit + 5; + } + for (int i = 0; i < num; i++) + { + IntVec3 first = target.Cell + array[i]; + Vector3 vector3 = first.ToVector3Shifted().Yto0(); + float second = Vector3.Dot((vector3 - vector).normalized, (vector3 - vector2).normalized); + tmpCellDots.Add(new Pair(first, second)); + } + tmpCellDots.SortByDescending(x => x.Second); + } + + Map map = Pawn.Map; + int num2 = Mathf.Min(tmpCellDots.Count, Props.numCellsToHit); + for (int j = 0; j < num2; j++) + { + IntVec3 first2 = tmpCellDots[j].First; + if (!first2.InBounds(map)) continue; + + if (first2.Filled(map)) + { + Building_Door door = first2.GetDoor(map); + if (door == null || !door.Open) continue; + } + + if (parent.verb.TryFindShootLineFromTo(Pawn.Position, first2, out var _, ignoreRange: true)) + { + tmpCells.Add(first2); + } + } + return tmpCells; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/CompProperties_AbilitySprayLiquidMulti.cs b/Source/ArachnaeSwarm/CompProperties_AbilitySprayLiquidMulti.cs new file mode 100644 index 0000000..8c62c43 --- /dev/null +++ b/Source/ArachnaeSwarm/CompProperties_AbilitySprayLiquidMulti.cs @@ -0,0 +1,17 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class CompProperties_AbilitySprayLiquidMulti : CompProperties_AbilitySprayLiquid + { + public int shotCount = 1; + + public int ticksBetweenShots = 10; + + public CompProperties_AbilitySprayLiquidMulti() + { + compClass = typeof(CompAbilityEffect_SprayLiquidMulti); + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Hediffs/Hediff_CurseFlame.cs b/Source/ArachnaeSwarm/Hediffs/Hediff_CurseFlame.cs new file mode 100644 index 0000000..90baad3 --- /dev/null +++ b/Source/ArachnaeSwarm/Hediffs/Hediff_CurseFlame.cs @@ -0,0 +1,197 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + public class CurseFlameModExt : DefModExtension + { + // XML 配置字段 + public string damageDefName = "AcidBurn"; // 默认 AcidBurn + public FloatRange damageRange = new FloatRange(1f, 3f); + public int damageIntervalTicks = 40; + + // 延迟加载的 DamageDef + private DamageDef _resolvedDamageDef; + public DamageDef ResolvedDamageDef + { + get + { + if (_resolvedDamageDef == null) + { + // 从 DefDatabase 解析 DamageDef + _resolvedDamageDef = DefDatabase.GetNamedSilentFail(damageDefName); + + // 回退逻辑:如果配置的 DefName 无效,使用 AcidBurn + if (_resolvedDamageDef == null) + { + Log.Error($"[DragonianMix] 未找到 DamageDef: {damageDefName}, 已回退到 AcidBurn"); + _resolvedDamageDef = DamageDefOf.AcidBurn; + } + } + return _resolvedDamageDef; + } + } + } + public class HediffCurseFlame : HediffWithComps + { + public override void Tick() + { + base.Tick(); + + // 1. 获取 ModExtension 配置 + var modExt = def.GetModExtension(); + if (modExt == null || pawn == null) return; + + // 2. 解析 DamageDef(此时 DefOf 已初始化) + DamageDef damageDef = modExt.ResolvedDamageDef; + if (damageDef == null) return; + + // 3. 触发伤害逻辑 + if (pawn.Spawned && !pawn.Dead && pawn.IsHashIntervalTick(modExt.damageIntervalTicks)) + { + var hittableParts = HittablePartsViolence(pawn.health.hediffSet).ToList(); + if (hittableParts.Any()) + { + DamageInfo damage = new DamageInfo( + def: damageDef, + amount: modExt.damageRange.RandomInRange, + armorPenetration: 999f, + hitPart: hittableParts.RandomElement(), + instigator: null + ); + pawn.TakeDamage(damage); + } + } + } + + public override void ExposeData() + { + base.ExposeData(); + Scribe_References.Look(ref parent, "parent", false); + } + + private static IEnumerable HittablePartsViolence(HediffSet bodyModel) + { + return bodyModel.GetNotMissingParts() + .Where(part => + part.coverageAbs > 0 && // 关键过滤:只选可被击中的部位 + (part.depth == BodyPartDepth.Outside || + (part.depth == BodyPartDepth.Inside && part.def.IsSolid(part, bodyModel.hediffs))) + ); + } + + public Pawn parent; + } + public class HediffComp_ContinuousDamage : HediffComp + { + // Token: 0x1700000E RID: 14 + // (get) Token: 0x0600004F RID: 79 RVA: 0x00003885 File Offset: 0x00001A85 + public HediffCompProperties_ContinuousDamage Props + { + get + { + return (HediffCompProperties_ContinuousDamage)this.props; + } + } + + // Token: 0x1700000F RID: 15 + // (get) Token: 0x06000050 RID: 80 RVA: 0x00003892 File Offset: 0x00001A92 + public override bool CompShouldRemove + { + get + { + return this.partMissing || base.CompShouldRemove; + } + } + + // Token: 0x06000051 RID: 81 RVA: 0x000038A4 File Offset: 0x00001AA4 + public override void CompPostMake() + { + base.CompPostMake(); + this.ticksToDamage = this.Props.ticksPerDamage; + } + + // Token: 0x06000052 RID: 82 RVA: 0x000038C0 File Offset: 0x00001AC0 + public override void CompPostTick(ref float severityAdjustment) + { + if (this.parent.IsTended() || this.parent.IsPermanent() || this.Props.endTicks <= (float)this.ticksNullify) + { + return; + } + this.ticksToDamage--; + this.ticksNullify++; + if (this.ticksToDamage <= 0) + { + this.ticksToDamage = this.Props.ticksPerDamage; + DamageDef damageDef = this.Props.damageDef; + DamageInfo dinfo = new DamageInfo(damageDef, this.Props.damageAmount, 0f, 0f, null, this.parent.Part, null, DamageInfo.SourceCategory.ThingOrUnknown, base.Pawn, true, true, QualityCategory.Normal, true); + dinfo.SetIgnoreArmor(true); + base.Pawn.TakeDamage(dinfo); + if (base.Pawn.health.hediffSet.PartIsMissing(this.parent.Part)) + { + this.partMissing = true; + } + } + } + + // Token: 0x06000053 RID: 83 RVA: 0x000039B4 File Offset: 0x00001BB4 + public override void CompPostMerged(Hediff other) + { + base.CompPostMerged(other); + HediffComp_ContinuousDamage hediffComp_ContinuousDamage = other.TryGetComp(); + if (hediffComp_ContinuousDamage != null) + { + this.ticksNullify = Mathf.Min(hediffComp_ContinuousDamage.ticksNullify, this.ticksNullify); + } + } + + // Token: 0x06000054 RID: 84 RVA: 0x000039E9 File Offset: 0x00001BE9 + public override void CompExposeData() + { + Scribe_Values.Look(ref this.ticksNullify, "ticksNullify", 0, false); + Scribe_Values.Look(ref this.ticksToDamage, "ticksToDamage", 0, false); + Scribe_Values.Look(ref this.partMissing, "HCPD_partMissing", false, false); + } + + // Token: 0x06000055 RID: 85 RVA: 0x00003A21 File Offset: 0x00001C21 + public override string CompDebugString() + { + return "ticksToDamage: " + this.ticksToDamage.ToString(); + } + + // Token: 0x0400002C RID: 44 + public int ticksNullify; + + // Token: 0x0400002D RID: 45 + public int ticksToDamage; + + // Token: 0x0400002E RID: 46 + public bool partMissing; + } + public class HediffCompProperties_ContinuousDamage : HediffCompProperties + { + // Token: 0x0600004E RID: 78 RVA: 0x0000384F File Offset: 0x00001A4F + public HediffCompProperties_ContinuousDamage() + { + this.compClass = typeof(HediffComp_ContinuousDamage); + } + + // Token: 0x04000028 RID: 40 + public DamageDef damageDef; + + // Token: 0x04000029 RID: 41 + public int ticksPerDamage = 100; + + // Token: 0x0400002A RID: 42 + public float endTicks = -1f; + + // Token: 0x0400002B RID: 43 + public float damageAmount = 1f; + } +} \ No newline at end of file