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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

锁定死神大招斩击中心并冻结战场

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

代码差异

4 个文件 +156 -10
Modified Common/ReaperCombatEntities.cs +68 -1
@@ -44,6 +44,17 @@ public sealed class ReaperCombatPlayer : ModPlayer
44 44
45 45 public bool BloodFrenzyActive => bloodFrenzyLatched;
46 46 internal int BloodShieldAmount => bloodShield;
47 internal bool FreezesWorldForDeathUltimate
48 {
49 get
50 {
51 if (ultimateForm != ReaperFormId.Death || ultimateTimer <= 0)
52 return false;
53 int elapsed = ReaperDeathUltimateGeometry.Duration - ultimateTimer;
54 return elapsed >= ReaperDeathUltimateGeometry.FirstCutTick
55 && elapsed <= ReaperDeathUltimateGeometry.ShatterTick;
56 }
57 }
47 58
48 59 public override void Initialize()
49 60 {
@@ -565,7 +576,7 @@ public sealed class ReaperCombatPlayer : ModPlayer
565 576 ReaperFormId.Frost => 120,
566 577 ReaperFormId.Soul => 116,
567 578 ReaperFormId.Void => 220,
568 ReaperFormId.Death => 170,
579 ReaperFormId.Death => ReaperDeathUltimateGeometry.Duration,
569 580 _ => 0
570 581 };
571 582 }
@@ -620,6 +631,11 @@ public sealed class ReaperCombatGlobalNPC : GlobalNPC
620 631 private Vector2 frostStoredVelocity;
621 632 private short deathUltimateStopTimer;
622 633 private Vector2 deathUltimateStoredVelocity;
634 private bool deathWorldFreezeCaptured;
635 private Vector2 deathWorldFreezeVelocity;
636 private float deathWorldFreezeRotation;
637 private Rectangle deathWorldFreezeFrame;
638 private double deathWorldFreezeFrameCounter;
623 639
624 640 public override void SetDefaults(NPC entity)
625 641 {
@@ -657,10 +673,44 @@ public sealed class ReaperCombatGlobalNPC : GlobalNPC
657 673 frostStoredVelocity = Vector2.Zero;
658 674 deathUltimateStopTimer = 0;
659 675 deathUltimateStoredVelocity = Vector2.Zero;
676 deathWorldFreezeCaptured = false;
677 deathWorldFreezeVelocity = Vector2.Zero;
678 deathWorldFreezeRotation = 0f;
679 deathWorldFreezeFrame = Rectangle.Empty;
680 deathWorldFreezeFrameCounter = 0d;
660 681 }
661 682
662 683 public override bool PreAI(NPC npc)
663 684 {
685 if (ReaperDeathUltimateFreezeSystem.IsWorldFrozen)
686 {
687 if (!deathWorldFreezeCaptured)
688 {
689 deathWorldFreezeCaptured = true;
690 deathWorldFreezeVelocity = npc.velocity;
691 deathWorldFreezeRotation = npc.rotation;
692 deathWorldFreezeFrame = npc.frame;
693 deathWorldFreezeFrameCounter = npc.frameCounter;
694 if (Main.netMode != NetmodeID.MultiplayerClient)
695 npc.netUpdate = true;
696 }
697 deathUltimateStopTimer = 0;
698 deathUltimateStoredVelocity = Vector2.Zero;
699 npc.velocity = Vector2.Zero;
700 npc.rotation = deathWorldFreezeRotation;
701 npc.frame = deathWorldFreezeFrame;
702 npc.frameCounter = deathWorldFreezeFrameCounter;
703 return false;
704 }
705 if (deathWorldFreezeCaptured)
706 {
707 npc.velocity = deathWorldFreezeVelocity;
708 deathWorldFreezeCaptured = false;
709 deathWorldFreezeVelocity = Vector2.Zero;
710 if (Main.netMode != NetmodeID.MultiplayerClient)
711 npc.netUpdate = true;
712 }
713
664 714 bool bossLike = IsBossLike(npc);
665 715 if (deathUltimateStopTimer > 0)
666 716 {
@@ -701,6 +751,17 @@ public sealed class ReaperCombatGlobalNPC : GlobalNPC
701 751 return true;
702 752 }
703 753
754 public override void FindFrame(NPC npc, int frameHeight)
755 {
756 if (!deathWorldFreezeCaptured
757 || !ReaperDeathUltimateFreezeSystem.IsWorldFrozen)
758 {
759 return;
760 }
761 npc.frame = deathWorldFreezeFrame;
762 npc.frameCounter = deathWorldFreezeFrameCounter;
763 }
764
704 765 internal void RecordDeathUltimateCut(NPC npc, int owner, int actionId, int damageDone)
705 766 {
706 767 if (owner < 0 || owner >= Main.maxPlayers || damageDone <= 0)
@@ -712,6 +773,12 @@ public sealed class ReaperCombatGlobalNPC : GlobalNPC
712 773 }
713 774 deathUltimateAccumulatedDamage[owner] = (int)Math.Min(int.MaxValue / 4L,
714 775 deathUltimateAccumulatedDamage[owner] + (long)damageDone);
776 if (ReaperDeathUltimateFreezeSystem.IsWorldFrozen)
777 {
778 npc.velocity = Vector2.Zero;
779 npc.netUpdate = true;
780 return;
781 }
715 782 if (deathUltimateStopTimer <= 0)
716 783 deathUltimateStoredVelocity = npc.velocity;
717 784 deathUltimateStopTimer = Math.Max(deathUltimateStopTimer, (short)180);
Added Common/ReaperDeathUltimateFreezeSystem.cs +49 -0
@@ -0,0 +1,49 @@
1 using Terraria;
2 using Terraria.ModLoader;
3
4 namespace DeathMod.Common;
5
6 /// <summary>
7 /// Resolves the authoritative Death Reaper world-stop once per game tick. NPC
8 /// hooks query this cached value, so a full battlefield does not rescan every
9 /// player once for every NPC.
10 /// </summary>
11 internal sealed class ReaperDeathUltimateFreezeSystem : ModSystem
12 {
13 private static ulong cachedTick = ulong.MaxValue;
14 private static bool cachedFrozen;
15
16 internal static bool IsWorldFrozen
17 {
18 get
19 {
20 if (Main.gameMenu)
21 return false;
22 if (cachedTick == Main.GameUpdateCount)
23 return cachedFrozen;
24
25 cachedTick = Main.GameUpdateCount;
26 cachedFrozen = false;
27 foreach (Player player in Main.ActivePlayers)
28 {
29 if (!player.dead && player.GetModPlayer<ReaperCombatPlayer>()
30 .FreezesWorldForDeathUltimate)
31 {
32 cachedFrozen = true;
33 break;
34 }
35 }
36 return cachedFrozen;
37 }
38 }
39
40 public override void OnWorldLoad() => ResetCache();
41 public override void OnWorldUnload() => ResetCache();
42 public override void Unload() => ResetCache();
43
44 private static void ResetCache()
45 {
46 cachedTick = ulong.MaxValue;
47 cachedFrozen = false;
48 }
49 }
Modified Common/ReaperUltimateVisualSystem.cs +18 -4
@@ -261,8 +261,11 @@ public sealed class ReaperUltimateVisualSystem : ModSystem
261 261 origin = viewport * 0.5f - state.Aim * 180f;
262 262 }
263 263 Vector2 focus = state.FocusWorld - Main.screenPosition;
264 if (focus.X < -presentationMargin || focus.X > viewport.X + presentationMargin
265 || focus.Y < -presentationMargin || focus.Y > viewport.Y + presentationMargin)
264 if (state.Form != ReaperFormId.Death
265 && (focus.X < -presentationMargin
266 || focus.X > viewport.X + presentationMargin
267 || focus.Y < -presentationMargin
268 || focus.Y > viewport.Y + presentationMargin))
266 269 {
267 270 focus = viewport * 0.5f;
268 271 }
@@ -1032,7 +1035,7 @@ public sealed class ReaperUltimateVisualSystem : ModSystem
1032 1035 Array.Sort(deathMirrorAngles);
1033 1036
1034 1037 float eased = Ease(shatter);
1035 float radius = viewport.Length() * 1.35f;
1038 float radius = GetViewportCoverRadius(focus, viewport) * 1.08f;
1036 1039 Color shardColor = Color.White
1037 1040 * (opacity * MathHelper.Lerp(1f, 0.48f, eased));
1038 1041 int vertex = 0;
@@ -1117,11 +1120,22 @@ public sealed class ReaperUltimateVisualSystem : ModSystem
1117 1120 float rotation)
1118 1121 => origin + (point - origin).RotatedBy(rotation);
1119 1122
1123 private static float GetViewportCoverRadius(Vector2 focus, Vector2 viewport)
1124 {
1125 float radius = focus.Length();
1126 radius = Math.Max(radius, Vector2.Distance(focus,
1127 new Vector2(viewport.X, 0f)));
1128 radius = Math.Max(radius, Vector2.Distance(focus,
1129 new Vector2(0f, viewport.Y)));
1130 radius = Math.Max(radius, Vector2.Distance(focus, viewport));
1131 return Math.Max(1f, radius);
1132 }
1133
1120 1134 private static void DrawDeathUltimateCutCracks(SpriteBatch batch,
1121 1135 Texture2D pixel, in UltimateVisualState state, Vector2 focus,
1122 1136 Vector2 viewport, float shatter, float opacity)
1123 1137 {
1124 float radius = viewport.Length() * 0.72f;
1138 float radius = GetViewportCoverRadius(focus, viewport) * 1.04f;
1125 1139 for (int cut = 0; cut < ReaperDeathUltimateGeometry.CutCount; cut++)
1126 1140 {
1127 1141 int cutTick = ReaperDeathUltimateGeometry.GetCutTick(cut);
Modified Projectiles/ReaperActionControllerProjectile.cs +21 -5
@@ -20,6 +20,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
20 20 private SickleCombatSnapshot snapshot;
21 21 private Vector2 aim;
22 22 private Vector2 specialTargetOffset;
23 private Vector2 ultimateFocusWorld;
23 24 private bool ultimate;
24 25 private bool configured;
25 26 private int timer;
@@ -73,7 +74,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
73 74 return -1;
74 75 Projectile projectile = Main.projectile[index];
75 76 if (projectile.ModProjectile is ReaperActionControllerProjectile controller)
76 controller.Configure(snapshot, requestedTarget, ultimate);
77 controller.Configure(snapshot, requestedTarget, ultimate, player.Center);
77 78 projectile.netUpdate = true;
78 79 return index;
79 80 }
@@ -199,6 +200,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
199 200 snapshot.Write(writer);
200 201 writer.WriteVector2(aim);
201 202 writer.WriteVector2(specialTargetOffset);
203 writer.WriteVector2(ultimateFocusWorld);
202 204 writer.Write(ultimate);
203 205 writer.Write(Math.Max(0, timer));
204 206 writer.Write(Math.Max(0, chargeFrames));
@@ -226,6 +228,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
226 228 SickleCombatSnapshot incomingSnapshot = SickleCombatSnapshot.Read(reader);
227 229 Vector2 incomingAim = reader.ReadVector2().SafeNormalize(Vector2.UnitX);
228 230 Vector2 incomingTargetOffset = reader.ReadVector2();
231 Vector2 incomingUltimateFocusWorld = reader.ReadVector2();
229 232 bool incomingUltimate = reader.ReadBoolean();
230 233 int incomingTimer = Math.Max(0, reader.ReadInt32());
231 234 int incomingChargeFrames = Math.Max(0, reader.ReadInt32());
@@ -250,6 +253,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
250 253 snapshot = incomingSnapshot;
251 254 aim = incomingAim;
252 255 specialTargetOffset = incomingTargetOffset;
256 ultimateFocusWorld = incomingUltimateFocusWorld;
253 257 ultimate = incomingUltimate;
254 258 timer = incomingTimer;
255 259 chargeFrames = incomingChargeFrames;
@@ -300,7 +304,8 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
300 304 end = reader.ReadVector2();
301 305 }
302 306
303 private void Configure(in SickleCombatSnapshot value, Vector2 direction, bool isUltimate)
307 private void Configure(in SickleCombatSnapshot value, Vector2 direction,
308 bool isUltimate, Vector2 playerCenter)
304 309 {
305 310 snapshot = value;
306 311 aim = direction.SafeNormalize(Vector2.UnitX);
@@ -312,6 +317,12 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
312 317 specialTargetOffset *= 2000f / targetDistance;
313 318 else if (targetDistance < 2f)
314 319 specialTargetOffset = aim * 240f;
320 ultimateFocusWorld = playerCenter + specialTargetOffset;
321 if (!float.IsFinite(ultimateFocusWorld.X)
322 || !float.IsFinite(ultimateFocusWorld.Y))
323 {
324 ultimateFocusWorld = playerCenter + aim * 240f;
325 }
315 326 ultimate = isUltimate;
316 327 configured = true;
317 328 timer = 0;
@@ -732,9 +743,14 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
732 743 _ => 1
733 744 };
734 745 Vector2 origin = player.MountedCenter;
735 Vector2 focus = snapshot.Form is ReaperFormId.Void or ReaperFormId.Death
736 ? player.Center + specialTargetOffset
737 : origin + aim * 180f;
746 Vector2 focus = snapshot.Form switch
747 {
748 // Death locks the selected point in world space at cast start. Player
749 // movement and camera movement must not drag the cut intersection.
750 ReaperFormId.Death => ultimateFocusWorld,
751 ReaperFormId.Void => player.Center + specialTargetOffset,
752 _ => origin + aim * 180f
753 };
738 754 ReaperUltimateVisualSystem.ReportUltimate(player, snapshot.Form, aim,
739 755 focus, Projectile.identity, timer, visualDuration);
740 756 switch (snapshot.Form)