XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/DeathMod

新增镰刀形态退阶与精华返还

a1e683f
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

7 个文件 +173 -16
Modified Common/DeathAltarUpgrade.cs +6 -2
@@ -8,6 +8,7 @@ public enum DeathAltarUpgradeType : byte
8 8 {
9 9 Advancement,
10 10 AlternateAdvancement,
11 Regression,
11 12 Damage,
12 13 ProjectileCount,
13 14 ProjectileDuration,
@@ -65,7 +66,8 @@ public enum DeathAltarUpgradeType : byte
65 66 public enum DeathAltarCurrency : byte
66 67 {
67 68 Souls,
68 SoulEssence
69 SoulEssence,
70 SoulEssenceRefund
69 71 }
70 72
71 73 public enum DeathAltarUnavailableReason : byte
@@ -75,7 +77,8 @@ public enum DeathAltarUnavailableReason : byte
75 77 RequiresWingMastery,
76 78 RequiresFatedUnlock,
77 79 RequiresNecklaceMastery,
78 RequiresAllSickleUpgrades
80 RequiresAllSickleUpgrades,
81 RequiresHigherSickleForm
79 82 }
80 83
81 84 public enum DeathAltarItemStorage : byte
@@ -113,6 +116,7 @@ public readonly record struct DeathAltarPrice(int Amount, DeathAltarCurrency Cur
113 116 public static DeathAltarPrice Unavailable => new(-1, DeathAltarCurrency.SoulEssence);
114 117 public static DeathAltarPrice Souls(int amount) => new(amount, DeathAltarCurrency.Souls);
115 118 public static DeathAltarPrice Essence(int amount) => new(amount, DeathAltarCurrency.SoulEssence);
119 public static DeathAltarPrice RefundEssence(int amount) => new(amount, DeathAltarCurrency.SoulEssenceRefund);
116 120 }
117 121
118 122 public interface IDeathAltarUpgradeable
Modified Common/SickleUpgradeService.cs +12 -1
@@ -85,12 +85,23 @@ internal static class DeathAltarUpgradeService
85 85 : [];
86 86 if (price.Currency == DeathAltarCurrency.Souls && !soulPlayer.TrySpendSouls(price.Amount))
87 87 return false;
88 if (price.Currency == DeathAltarCurrency.SoulEssenceRefund)
89 {
90 player.QuickSpawnItem(
91 player.GetSource_Misc("DeathMod:SickleRegression"),
92 essenceType,
93 price.Amount);
94 }
88 95
89 96 SyncUpgradeableItem(player, itemReference);
90 97 foreach (int slot in changedEssenceSlots)
91 98 SyncInventorySlot(player, slot);
92 99
93 DeathMod.SendOperationResult(player, DeathMod.OperationResult.UpgradeSucceeded);
100 DeathMod.SendOperationResult(
101 player,
102 price.Currency == DeathAltarCurrency.SoulEssenceRefund
103 ? DeathMod.OperationResult.SickleRegressed
104 : DeathMod.OperationResult.UpgradeSucceeded);
94 105 return true;
95 106 }
96 107
Modified DeathMod.cs +5 -1
@@ -42,6 +42,7 @@ public class DeathMod : Mod
42 42 EssenceAbsorbed,
43 43 NotEnoughSouls,
44 44 UpgradeSucceeded,
45 SickleRegressed,
45 46 NotEnoughEssence,
46 47 NotEnoughUpgradeSouls,
47 48 HoldSickle,
@@ -594,7 +595,10 @@ public class DeathMod : Mod
594 595 private static void ShowOperationResult(OperationResult result)
595 596 {
596 597 string key = $"Mods.DeathMod.Messages.{result}";
597 Color color = result is OperationResult.EssenceExtracted or OperationResult.EssenceAbsorbed or OperationResult.UpgradeSucceeded
598 Color color = result is OperationResult.EssenceExtracted
599 or OperationResult.EssenceAbsorbed
600 or OperationResult.UpgradeSucceeded
601 or OperationResult.SickleRegressed
598 602 ? new Color(90, 235, 255)
599 603 : new Color(255, 105, 130);
600 604 Main.NewText(Language.GetTextValue(key), color);
Modified Items/NormalSickle.cs +115 -8
@@ -69,7 +69,8 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
69 69 DeathAltarUpgradeType.FatedUnlock,
70 70 DeathAltarUpgradeType.FatedMaxStacks,
71 71 DeathAltarUpgradeType.FatedStacksPerHit,
72 DeathAltarUpgradeType.FatedDuration
72 DeathAltarUpgradeType.FatedDuration,
73 DeathAltarUpgradeType.Regression
73 74 ];
74 75
75 76 private static readonly DeathAltarUpgradeType[] BranchingUpgradeTypes =
@@ -87,7 +88,8 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
87 88 DeathAltarUpgradeType.FatedUnlock,
88 89 DeathAltarUpgradeType.FatedMaxStacks,
89 90 DeathAltarUpgradeType.FatedStacksPerHit,
90 DeathAltarUpgradeType.FatedDuration
91 DeathAltarUpgradeType.FatedDuration,
92 DeathAltarUpgradeType.Regression
91 93 ];
92 94
93 95 public int DamageLevel { get; internal set; } = 1;
@@ -102,6 +104,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
102 104 public int FatedMaximumStacksLevel { get; internal set; } = 1;
103 105 public int FatedStacksPerHitLevel { get; internal set; } = 1;
104 106 public int FatedDurationLevel { get; internal set; } = 1;
107 private int previousTierOneType;
108 private int previousTierTwoType;
109 private int previousTierThreeType;
105 110
106 111 public virtual int SickleTier => 0;
107 112 public virtual int BaseSickleDamage => 1;
@@ -341,6 +346,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
341 346 DeathAltarUpgradeType.AlternateAdvancement => AlternateNextSickleType == ItemID.None
342 347 ? DeathAltarPrice.Unavailable
343 348 : DeathAltarPrice.Essence(GetAdvancementEssenceCost()),
349 DeathAltarUpgradeType.Regression => SickleTier <= 0
350 ? DeathAltarPrice.Unavailable
351 : DeathAltarPrice.RefundEssence(GetRegressionEssenceRefund()),
344 352 DeathAltarUpgradeType.Damage => DamageLevel >= MaxDamageLevel
345 353 ? DeathAltarPrice.Unavailable
346 354 : DamageLevel < 20
@@ -387,16 +395,19 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
387 395
388 396 public bool ApplyAltarUpgrade(Item item, DeathAltarUpgradeType upgradeType)
389 397 {
390 return upgradeType is DeathAltarUpgradeType.Advancement or DeathAltarUpgradeType.AlternateAdvancement
391 ? Advance(item, upgradeType)
392 : ApplyUpgrade(upgradeType);
398 return upgradeType switch
399 {
400 DeathAltarUpgradeType.Advancement or DeathAltarUpgradeType.AlternateAdvancement => Advance(item, upgradeType),
401 DeathAltarUpgradeType.Regression => Regress(item),
402 _ => ApplyUpgrade(upgradeType)
403 };
393 404 }
394 405
395 406 public string GetCurrentUpgradeValue(DeathAltarUpgradeType upgradeType)
396 407 {
397 408 return upgradeType switch
398 409 {
399 DeathAltarUpgradeType.Advancement or DeathAltarUpgradeType.AlternateAdvancement => Item.Name,
410 DeathAltarUpgradeType.Advancement or DeathAltarUpgradeType.AlternateAdvancement or DeathAltarUpgradeType.Regression => Item.Name,
400 411 DeathAltarUpgradeType.Damage => Language.GetTextValue("Mods.DeathMod.UI.UpgradeValues.Damage", DamageLevel, ScaledBaseDamage),
401 412 DeathAltarUpgradeType.AttackSpeed => Language.GetTextValue("Mods.DeathMod.UI.UpgradeValues.Frames", AttackUseTime),
402 413 DeathAltarUpgradeType.Knockback => TotalBaseKnockback.ToString("0.0"),
@@ -419,6 +430,7 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
419 430 {
420 431 DeathAltarUpgradeType.Advancement => Lang.GetItemNameValue(PrimaryNextSickleType),
421 432 DeathAltarUpgradeType.AlternateAdvancement => Lang.GetItemNameValue(AlternateNextSickleType),
433 DeathAltarUpgradeType.Regression => Lang.GetItemNameValue(GetPreviousSickleType()),
422 434 DeathAltarUpgradeType.Damage => Language.GetTextValue("Mods.DeathMod.UI.UpgradeValues.Damage", DamageLevel + 1, GetScaledDamage(DamageLevel + 1)),
423 435 DeathAltarUpgradeType.AttackSpeed => Language.GetTextValue("Mods.DeathMod.UI.UpgradeValues.Frames", AttackUseTime - 1),
424 436 DeathAltarUpgradeType.Knockback => (TotalBaseKnockback + 0.5f).ToString("0.0"),
@@ -444,6 +456,8 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
444 456 {
445 457 return DeathAltarUnavailableReason.RequiresAllSickleUpgrades;
446 458 }
459 if (upgradeType == DeathAltarUpgradeType.Regression && SickleTier <= 0)
460 return DeathAltarUnavailableReason.RequiresHigherSickleForm;
447 461 if ((upgradeType is DeathAltarUpgradeType.ProjectileCount or DeathAltarUpgradeType.ProjectileDuration or DeathAltarUpgradeType.HitCooldown) && !SupportsProjectileCount)
448 462 return DeathAltarUnavailableReason.RequiresProjectile;
449 463 if ((upgradeType is DeathAltarUpgradeType.FatedMaxStacks or DeathAltarUpgradeType.FatedStacksPerHit or DeathAltarUpgradeType.FatedDuration) && !FatedUnlocked)
@@ -545,7 +559,22 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
545 559 int nextType = advancementType == DeathAltarUpgradeType.AlternateAdvancement
546 560 ? AlternateNextSickleType
547 561 : PrimaryNextSickleType;
548 if (nextType == ItemID.None)
562 return TransformSickle(item, nextType, recordCurrentForm: true);
563 }
564
565 private bool Regress(Item item)
566 {
567 if (SickleTier <= 0)
568 return false;
569 return TransformSickle(item, GetPreviousSickleType(), recordCurrentForm: false);
570 }
571
572 private bool TransformSickle(Item item, int targetType, bool recordCurrentForm)
573 {
574 if (targetType <= ItemID.None || ItemLoader.GetItem(targetType) is not NormalSickle targetTemplate)
575 return false;
576 int expectedTier = SickleTier + (recordCurrentForm ? 1 : -1);
577 if (targetTemplate.SickleTier != expectedTier)
549 578 return false;
550 579
551 580 int damageLevel = DamageLevel;
@@ -560,10 +589,29 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
560 589 int fatedMaximumStacksLevel = FatedMaximumStacksLevel;
561 590 int fatedStacksPerHitLevel = FatedStacksPerHitLevel;
562 591 int fatedDurationLevel = FatedDurationLevel;
592 int tierOneHistory = previousTierOneType;
593 int tierTwoHistory = previousTierTwoType;
594 int tierThreeHistory = previousTierThreeType;
595 int currentType = item.type;
596 if (recordCurrentForm)
597 {
598 switch (SickleTier)
599 {
600 case 1:
601 tierOneHistory = currentType;
602 break;
603 case 2:
604 tierTwoHistory = currentType;
605 break;
606 case 3:
607 tierThreeHistory = currentType;
608 break;
609 }
610 }
563 611 int prefix = item.prefix;
564 612 bool favorited = item.favorited;
565 613
566 item.SetDefaults(nextType);
614 item.SetDefaults(targetType);
567 615 if (prefix > 0)
568 616 item.Prefix(prefix);
569 617
@@ -582,11 +630,45 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
582 630 upgraded.FatedMaximumStacksLevel = fatedMaximumStacksLevel;
583 631 upgraded.FatedStacksPerHitLevel = fatedStacksPerHitLevel;
584 632 upgraded.FatedDurationLevel = fatedDurationLevel;
633 upgraded.previousTierOneType = tierOneHistory;
634 upgraded.previousTierTwoType = tierTwoHistory;
635 upgraded.previousTierThreeType = tierThreeHistory;
585 636 upgraded.ApplyDynamicStats();
586 637 item.favorited = favorited;
587 638 return true;
588 639 }
589 640
641 private int GetPreviousSickleType()
642 {
643 int storedType = SickleTier switch
644 {
645 2 => previousTierOneType,
646 3 => previousTierTwoType,
647 4 => previousTierThreeType,
648 _ => ItemID.None
649 };
650 if (storedType > ItemID.None
651 && ItemLoader.GetItem(storedType) is NormalSickle storedSickle
652 && storedSickle.SickleTier == SickleTier - 1)
653 {
654 return storedType;
655 }
656
657 // Items created before regression history existed use a deterministic
658 // canonical predecessor. Newly advanced items always return along the exact
659 // branch the player originally chose.
660 return this switch
661 {
662 SkullSickle or BloodSickle => ModContent.ItemType<NormalSickle>(),
663 FrostSickle => ModContent.ItemType<BloodSickle>(),
664 DevilSickle => ModContent.ItemType<SkullSickle>(),
665 SoulSickle => ModContent.ItemType<DevilSickle>(),
666 VoidSickle => ModContent.ItemType<FrostSickle>(),
667 DeathSickle => ModContent.ItemType<SoulSickle>(),
668 _ => ItemID.None
669 };
670 }
671
590 672 public override void SaveData(TagCompound tag)
591 673 {
592 674 tag[nameof(DamageLevel)] = DamageLevel;
@@ -601,6 +683,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
601 683 tag[nameof(FatedMaximumStacksLevel)] = FatedMaximumStacksLevel;
602 684 tag[nameof(FatedStacksPerHitLevel)] = FatedStacksPerHitLevel;
603 685 tag[nameof(FatedDurationLevel)] = FatedDurationLevel;
686 tag[nameof(previousTierOneType)] = previousTierOneType;
687 tag[nameof(previousTierTwoType)] = previousTierTwoType;
688 tag[nameof(previousTierThreeType)] = previousTierThreeType;
604 689 }
605 690
606 691 public override void LoadData(TagCompound tag)
@@ -617,6 +702,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
617 702 FatedMaximumStacksLevel = Math.Clamp(tag.GetInt(nameof(FatedMaximumStacksLevel)), 1, MaxFatedStackLevel);
618 703 FatedStacksPerHitLevel = Math.Clamp(tag.GetInt(nameof(FatedStacksPerHitLevel)), 1, MaxFatedStacksPerHitLevel);
619 704 FatedDurationLevel = Math.Clamp(tag.GetInt(nameof(FatedDurationLevel)), 1, MaxFatedDurationLevel);
705 previousTierOneType = tag.GetInt(nameof(previousTierOneType));
706 previousTierTwoType = tag.GetInt(nameof(previousTierTwoType));
707 previousTierThreeType = tag.GetInt(nameof(previousTierThreeType));
620 708 ApplyDynamicStats();
621 709 }
622 710
@@ -634,6 +722,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
634 722 writer.Write((byte)FatedMaximumStacksLevel);
635 723 writer.Write((byte)FatedStacksPerHitLevel);
636 724 writer.Write((byte)FatedDurationLevel);
725 writer.Write(previousTierOneType);
726 writer.Write(previousTierTwoType);
727 writer.Write(previousTierThreeType);
637 728 }
638 729
639 730 public override void NetReceive(BinaryReader reader)
@@ -650,6 +741,9 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
650 741 FatedMaximumStacksLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxFatedStackLevel);
651 742 FatedStacksPerHitLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxFatedStacksPerHitLevel);
652 743 FatedDurationLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxFatedDurationLevel);
744 previousTierOneType = reader.ReadInt32();
745 previousTierTwoType = reader.ReadInt32();
746 previousTierThreeType = reader.ReadInt32();
653 747 ApplyDynamicStats();
654 748 }
655 749
@@ -711,6 +805,19 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
711 805 };
712 806 }
713 807
808 private int GetRegressionEssenceRefund()
809 {
810 // Refund exactly the cost paid to enter the current form.
811 return SickleTier switch
812 {
813 1 => 1,
814 2 => 16,
815 3 => 32,
816 4 => 99,
817 _ => 0
818 };
819 }
820
714 821 private void SpawnLegacyProjectiles(IEntitySource source, Vector2 position, Vector2 velocity, int type, int damage, float knockback, int owner)
715 822 {
716 823 if (!SupportsProjectileCount || type <= ProjectileID.None)
Modified Localization/en-US.hjson +4 -0
@@ -185,6 +185,7 @@ Mods: {
185 185 UpgradeCosts: {
186 186 Souls: "{0} character souls"
187 187 SoulEssence: "{0} Soul Essence"
188 SoulEssenceRefund: "Refunds {0} Soul Essence"
188 189 }
189 190
190 191 UpgradeValues: {
@@ -202,11 +203,13 @@ Mods: {
202 203 RequiresFatedUnlock: Unlock Fated first
203 204 RequiresNecklaceMastery: Max all five domain attributes first
204 205 RequiresAllSickleUpgrades: Max every other upgrade on this sickle before entering form 5
206 RequiresHigherSickleForm: The initial form cannot regress any further
205 207 }
206 208
207 209 UpgradeNames: {
208 210 Advancement: Primary-form advancement
209 211 AlternateAdvancement: Branch-form advancement
212 Regression: Regress to previous form
210 213 Damage: Damage modification
211 214 AttackSpeed: Attack speed
212 215 Knockback: Knockback
@@ -269,6 +272,7 @@ Mods: {
269 272 EssenceAbsorbed: Absorbed 1 Soul Essence and gained 100 souls.
270 273 NotEnoughSouls: Not enough souls: extracting Soul Essence requires 100 souls.
271 274 UpgradeSucceeded: Equipment modification complete.
275 SickleRegressed: The sickle returned to its previous form and its advancement Soul Essence was refunded.
272 276 NotEnoughEssence: Not enough Soul Essence.
273 277 NotEnoughUpgradeSouls: Not enough character souls for this low-tier infusion.
274 278 HoldSickle: The selected altar item is no longer in that slot; select it again.
Modified Localization/zh-Hans.hjson +4 -0
@@ -185,6 +185,7 @@ Mods: {
185 185 UpgradeCosts: {
186 186 Souls: "{0} 人物灵魂"
187 187 SoulEssence: "{0} 灵魂精华"
188 SoulEssenceRefund: "返还 {0} 灵魂精华"
188 189 }
189 190
190 191 UpgradeValues: {
@@ -202,11 +203,13 @@ Mods: {
202 203 RequiresFatedUnlock: 需先点亮命定
203 204 RequiresNecklaceMastery: 需先将五项领域能力升至满级
204 205 RequiresAllSickleUpgrades: 进阶第5形态前,需先将当前镰刀的其它升级全部升满
206 RequiresHigherSickleForm: 初始形态无法继续退阶
205 207 }
206 208
207 209 UpgradeNames: {
208 210 Advancement: 主形态进阶
209 211 AlternateAdvancement: 分支形态进阶
212 Regression: 退回上一形态
210 213 Damage: 攻击改造
211 214 AttackSpeed: 攻速改造
212 215 Knockback: 击退改造
@@ -269,6 +272,7 @@ Mods: {
269 272 EssenceAbsorbed: 已吸收1个灵魂精华并获得100灵魂。
270 273 NotEnoughSouls: 灵魂不足:提取灵魂精华需要100灵魂。
271 274 UpgradeSucceeded: 装备改造完成。
275 SickleRegressed: 镰刀已退回上一形态,进阶消耗的灵魂精华已返还。
272 276 NotEnoughEssence: 灵魂精华不足。
273 277 NotEnoughUpgradeSouls: 人物灵魂不足,无法完成这次低阶注魂。
274 278 HoldSickle: 祭坛所选物品已不在原槽位,请重新选择。
Modified UI/DeathAltarUISystem.cs +27 -4
@@ -362,7 +362,7 @@ internal class DeathAltarUIState : UIState
362 362 for (int index = 0; index < count; index++)
363 363 {
364 364 AltarSkillNode node = new(types[first + index], this, index);
365 GetNodePosition(index, fatedIndex, convergenceRootIndex, out int column, out int row);
365 GetNodePosition(types[first + index], index, fatedIndex, convergenceRootIndex, out int column, out int row);
366 366 float left = convergenceRootIndex == 4 && index < convergenceRootIndex
367 367 ? TreeLeft + 18f + index * 137f
368 368 : TreeLeft + column * 109f;
@@ -383,8 +383,21 @@ internal class DeathAltarUIState : UIState
383 383 displayedPage = currentPage;
384 384 }
385 385
386 private static void GetNodePosition(int index, int fatedIndex, int convergenceRootIndex, out int column, out int row)
386 private static void GetNodePosition(
387 DeathAltarUpgradeType type,
388 int index,
389 int fatedIndex,
390 int convergenceRootIndex,
391 out int column,
392 out int row)
387 393 {
394 if (type == DeathAltarUpgradeType.Regression)
395 {
396 column = 4;
397 row = 2;
398 return;
399 }
400
388 401 if (convergenceRootIndex >= 0)
389 402 {
390 403 column = index == convergenceRootIndex ? 2 : index;
@@ -430,7 +443,12 @@ internal class DeathAltarUIState : UIState
430 443
431 444 internal static string FormatPrice(DeathAltarPrice price)
432 445 {
433 string key = price.Currency == DeathAltarCurrency.Souls ? "Souls" : "SoulEssence";
446 string key = price.Currency switch
447 {
448 DeathAltarCurrency.Souls => "Souls",
449 DeathAltarCurrency.SoulEssenceRefund => "SoulEssenceRefund",
450 _ => "SoulEssence"
451 };
434 452 return Language.GetTextValue($"Mods.DeathMod.UI.UpgradeCosts.{key}", price.Amount);
435 453 }
436 454 }
@@ -519,7 +537,12 @@ internal sealed class AltarSkillNode : UIPanel
519 537 DeathAltarPrice price = upgradeable?.GetUpgradePrice(UpgradeType) ?? DeathAltarPrice.Unavailable;
520 538 maxed = !price.IsAvailable && upgradeable?.GetUnavailableReason(UpgradeType) == DeathAltarUnavailableReason.Maxed;
521 539 canRequest = price.IsAvailable;
522 bool affordable = price.Currency == DeathAltarCurrency.Souls ? souls >= price.Amount : essenceCount >= price.Amount;
540 bool affordable = price.Currency switch
541 {
542 DeathAltarCurrency.Souls => souls >= price.Amount,
543 DeathAltarCurrency.SoulEssence => essenceCount >= price.Amount,
544 _ => true
545 };
523 546 Lit = maxed || canRequest && affordable;
524 547 string detail;
525 548 if (upgradeable is null)