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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

统一死亡领域背景缩放与世界采样

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

代码差异

2 个文件 +145 -119
Modified Common/DeathDomainBackdropTextureSystem.cs +106 -19
@@ -22,6 +22,14 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
22 22 * (DeathNecklace.MaxCoreLevel - 1)) * 2f;
23 23
24 24 private static readonly Texture2D?[] layers = new Texture2D?[3];
25 private static readonly float[] expandedSourceX = new float[3];
26 private static readonly float[] expandedSourceY = new float[3];
27 private static readonly float[] expandedSourceWidth = new float[3];
28 private static readonly float[] expandedSourceHeight = new float[3];
29 private static ulong expandedFrameTick = ulong.MaxValue;
30 private static Vector2 expandedDestination;
31 private static float expandedDestinationWidth = 1f;
32 private static float expandedDestinationHeight = 1f;
25 33
26 34 internal static Texture2D GetLayer(int index)
27 35 {
@@ -29,6 +37,83 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
29 37 return layers[index] ??= CreateLayer(index);
30 38 }
31 39
40 /// <summary>
41 /// Returns the single world-space frame used by a fully manifested Death
42 /// Domain. Every smaller mask (necklace aperture, weapon wound and wing
43 /// trail) samples this frame and merely clips it, preventing the scenery
44 /// inside a cut from being enlarged to fit that cut.
45 /// </summary>
46 internal static void GetExpandedDomainSourceFrame(int layer,
47 out float sourceOffsetX, out float sourceOffsetY,
48 out float viewWidth, out float viewHeight)
49 {
50 EnsureExpandedDomainFrame();
51 layer = Math.Clamp(layer, 0, layers.Length - 1);
52 sourceOffsetX = expandedSourceX[layer];
53 sourceOffsetY = expandedSourceY[layer];
54 viewWidth = expandedSourceWidth[layer];
55 viewHeight = expandedSourceHeight[layer];
56 }
57
58 /// <summary>
59 /// Returns the rectangle occupied by the fully manifested domain in the
60 /// active world SpriteBatch's pre-transform coordinate space. This is also
61 /// the normalization frame for every world-space domain mask, so game zoom
62 /// cannot make weapon or wing cuts drift away from the necklace backdrop.
63 /// </summary>
64 internal static void GetExpandedDomainDrawBounds(
65 out Vector2 destination,
66 out float destinationWidth,
67 out float destinationHeight)
68 {
69 EnsureExpandedDomainFrame();
70 destination = expandedDestination;
71 destinationWidth = expandedDestinationWidth;
72 destinationHeight = expandedDestinationHeight;
73 }
74
75 private static void EnsureExpandedDomainFrame()
76 {
77 // A crescent or merged trail can contain thousands of vertices. Cache
78 // the inverse view transform and all three source plates once per game
79 // update instead of repeating a matrix inversion for every vertex.
80 if (expandedFrameTick == Main.GameUpdateCount)
81 return;
82 expandedFrameTick = Main.GameUpdateCount;
83
84 Matrix inverse = Matrix.Invert(Main.GameViewMatrix.TransformationMatrix);
85 Vector2 topLeft = Vector2.Transform(Vector2.Zero, inverse);
86 Vector2 topRight = Vector2.Transform(
87 new Vector2(Main.screenWidth, 0f), inverse);
88 Vector2 bottomLeft = Vector2.Transform(
89 new Vector2(0f, Main.screenHeight), inverse);
90 Vector2 bottomRight = Vector2.Transform(
91 new Vector2(Main.screenWidth, Main.screenHeight), inverse);
92 float left = Math.Min(Math.Min(topLeft.X, topRight.X),
93 Math.Min(bottomLeft.X, bottomRight.X));
94 float right = Math.Max(Math.Max(topLeft.X, topRight.X),
95 Math.Max(bottomLeft.X, bottomRight.X));
96 float top = Math.Min(Math.Min(topLeft.Y, topRight.Y),
97 Math.Min(bottomLeft.Y, bottomRight.Y));
98 float bottom = Math.Max(Math.Max(topLeft.Y, topRight.Y),
99 Math.Max(bottomLeft.Y, bottomRight.Y));
100 expandedDestination = new Vector2(left, top);
101 expandedDestinationWidth = Math.Max(1f, right - left);
102 expandedDestinationHeight = Math.Max(1f, bottom - top);
103
104 Vector2 sourceAperture = new(
105 Math.Max(1, Main.screenWidth),
106 Math.Max(1, Main.screenHeight));
107 Vector2 sourceAnchor = Main.screenPosition + sourceAperture * 0.5f;
108 for (int layer = 0; layer < layers.Length; layer++)
109 {
110 GetSourceFrame(layer, sourceAnchor, sourceAperture,
111 out expandedSourceX[layer], out expandedSourceY[layer],
112 out expandedSourceWidth[layer],
113 out expandedSourceHeight[layer]);
114 }
115 }
116
32 117 /// <summary>
33 118 /// Maps a world point into the exact parallax plate framing used by the Death
34 119 /// Necklace. Weapon crescents and persistent wounds use this instead of raw
@@ -56,25 +141,29 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
56 141 new Vector2(ReferenceDomainDiameter));
57 142
58 143 /// <summary>
59 /// Uses the active owner's Death Necklace aperture when one exists. A
60 /// descended domain is sampled against exactly the current viewport; a
61 /// circular domain uses the same reference aperture as the necklace. Every
62 /// weapon mask belonging to that owner therefore reveals the same parallax
63 /// pixel at the same world position.
144 /// Uses the fully manifested Death Necklace framing at all times. The owner
145 /// and fallback anchor remain part of the call contract because masks are
146 /// owner-scoped, but neither may rescale or recenter the shared backdrop.
147 /// Every mask therefore reveals the same parallax pixel at the same visible
148 /// world position before, during and after manifestation.
64 149 /// </summary>
65 150 internal static Vector2 GetMatchingDomainUv(int layer, Vector2 world,
66 151 int owner, Vector2 fallbackAnchor)
67 152 {
68 Vector2 anchor = fallbackAnchor;
69 Vector2 aperture = new(ReferenceDomainDiameter);
70 if (owner >= 0 && owner < Main.maxPlayers
71 && Main.player[owner] is Player player && player.active)
72 {
73 anchor = player.Center;
74 DeathDomainVisualSystem.TryGetMatchingBackdropAperture(owner,
75 out anchor, out aperture);
76 }
77 return GetWorldUv(layer, world, anchor, aperture);
153 _ = owner;
154 _ = fallbackAnchor;
155 GetExpandedDomainDrawBounds(out Vector2 destination,
156 out float destinationWidth, out float destinationHeight);
157 GetExpandedDomainSourceFrame(layer,
158 out float sourceOffsetX, out float sourceOffsetY,
159 out float viewWidth, out float viewHeight);
160 Vector2 worldTopLeft = Main.screenPosition + destination;
161 Vector2 normalized = new(
162 (world.X - worldTopLeft.X) / destinationWidth,
163 (world.Y - worldTopLeft.Y) / destinationHeight);
164 return new Vector2(
165 (sourceOffsetX + normalized.X * viewWidth) / LayerWidth,
166 (sourceOffsetY + normalized.Y * viewHeight) / LayerHeight);
78 167 }
79 168
80 169 internal static float GetLayerOpacity(int layer)
@@ -155,13 +244,10 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
155 244 return;
156 245 }
157 246
158 Vector2 worldCenter = Main.screenPosition
159 + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
160 Vector2 aperture = new(Main.screenWidth, Main.screenHeight);
161 247 for (int layer = 0; layer < 3; layer++)
162 248 {
163 249 Texture2D texture = GetLayer(layer);
164 GetSourceFrame(layer, worldCenter, aperture,
250 GetExpandedDomainSourceFrame(layer,
165 251 out float sourceX, out float sourceY,
166 252 out float sourceWidth, out float sourceHeight);
167 253 int width = Math.Clamp((int)MathF.Round(sourceWidth), 1,
@@ -228,6 +314,7 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
228 314
229 315 public override void Unload()
230 316 {
317 expandedFrameTick = ulong.MaxValue;
231 318 Texture2D?[] oldLayers = (Texture2D?[])layers.Clone();
232 319 Array.Clear(layers);
233 320 if (Main.dedServ)
Modified Common/DeathDomainVisualSystem.cs +39 -100
@@ -84,37 +84,6 @@ internal class DeathDomainVisualSystem : ModSystem
84 84 ClearAnimationState();
85 85 }
86 86
87 /// <summary>
88 /// Supplies the same aperture currently used to draw an owner's necklace
89 /// domain. Weapon masks query this instead of re-deriving full-screen state,
90 /// so future domain unlock rules cannot make the two backgrounds diverge.
91 /// </summary>
92 internal static bool TryGetMatchingBackdropAperture(int owner,
93 out Vector2 anchorWorld, out Vector2 apertureSize)
94 {
95 anchorWorld = owner >= 0 && owner < Main.maxPlayers
96 && Main.player[owner].active
97 ? Main.player[owner].Center
98 : Main.screenPosition
99 + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
100 apertureSize = new Vector2(
101 DeathDomainBackdropTextureSystem.ReferenceDomainDiameter);
102 if (owner < 0 || owner >= Main.maxPlayers)
103 return false;
104
105 DeathDomainVisualSystem instance = ModContent
106 .GetInstance<DeathDomainVisualSystem>();
107 if (instance.domainAnimation[owner] <= 0.001f)
108 return false;
109 if (instance.descentAnimation[owner] >= 0.999f)
110 {
111 anchorWorld = Main.screenPosition
112 + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
113 apertureSize = new Vector2(Main.screenWidth, Main.screenHeight);
114 }
115 return true;
116 }
117
118 87 public override void PostDrawTiles()
119 88 {
120 89 Player localPlayer = Main.LocalPlayer;
@@ -301,7 +270,14 @@ internal class DeathDomainVisualSystem : ModSystem
301 270
302 271 int bands = Math.Max(1, (int)Math.Ceiling(extent * 2f / preferredBandHeight));
303 272 float bandHeight = extent * 2f / bands;
304 Vector2 worldCenter = center + Main.screenPosition;
273 DeathDomainBackdropTextureSystem.GetExpandedDomainDrawBounds(
274 out Vector2 expandedDestination,
275 out float expandedDestinationWidth,
276 out float expandedDestinationHeight);
277 float expandedDestinationRight = expandedDestination.X
278 + expandedDestinationWidth;
279 float expandedDestinationBottom = expandedDestination.Y
280 + expandedDestinationHeight;
305 281 // Draw one complete layer at a time. This retains proper alpha compositing
306 282 // while allowing SpriteBatch to group hundreds of clipped scan bands into
307 283 // only three texture batches instead of switching textures for every row.
@@ -314,8 +290,9 @@ internal class DeathDomainVisualSystem : ModSystem
314 290 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
315 291 };
316 292 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
317 GetBackdropSource(layer, texture, worldCenter, 1f, 1f, out float sourceOffsetX,
318 out float sourceOffsetY, out float viewWidth, out float viewHeight);
293 DeathDomainBackdropTextureSystem.GetExpandedDomainSourceFrame(layer,
294 out float sourceOffsetX, out float sourceOffsetY,
295 out float viewWidth, out float viewHeight);
319 296
320 297 for (int index = 0; index < bands; index++)
321 298 {
@@ -338,16 +315,31 @@ internal class DeathDomainVisualSystem : ModSystem
338 315 if (left >= right)
339 316 continue;
340 317
341 float normalizedLeft = MathHelper.Clamp((left + extent) / (extent * 2f), 0f, 1f);
342 float normalizedRight = MathHelper.Clamp((right + extent) / (extent * 2f), 0f, 1f);
343 float normalizedTop = MathHelper.Clamp((scanY - bandHeight * 0.5f + extent) / (extent * 2f), 0f, 1f);
344 float normalizedBottom = MathHelper.Clamp((scanY + bandHeight * 0.5f + extent) / (extent * 2f), 0f, 1f);
318 float drawLeft = Math.Max(expandedDestination.X,
319 center.X + left);
320 float drawRight = Math.Min(expandedDestinationRight,
321 center.X + right);
322 float drawTop = Math.Max(expandedDestination.Y,
323 center.Y + scanY - bandHeight * 0.5f);
324 float drawBottom = Math.Min(expandedDestinationBottom,
325 center.Y + scanY + bandHeight * 0.5f);
326 if (drawLeft >= drawRight || drawTop >= drawBottom)
327 continue;
328
329 float normalizedLeft = (drawLeft - expandedDestination.X)
330 / expandedDestinationWidth;
331 float normalizedRight = (drawRight - expandedDestination.X)
332 / expandedDestinationWidth;
333 float normalizedTop = (drawTop - expandedDestination.Y)
334 / expandedDestinationHeight;
335 float normalizedBottom = (drawBottom - expandedDestination.Y)
336 / expandedDestinationHeight;
345 337 DrawBackdropBand(
346 338 batch,
347 339 texture,
348 new Vector2(center.X + left, center.Y + scanY - bandHeight * 0.5f),
349 right - left,
350 bandHeight,
340 new Vector2(drawLeft, drawTop),
341 drawRight - drawLeft,
342 drawBottom - drawTop,
351 343 sourceOffsetX + normalizedLeft * viewWidth,
352 344 sourceOffsetY + normalizedTop * viewHeight,
353 345 Math.Max(0.5f, (normalizedRight - normalizedLeft) * viewWidth),
@@ -608,7 +600,7 @@ internal class DeathDomainVisualSystem : ModSystem
608 600 bool drawBackdrop,
609 601 bool drawBoundary)
610 602 {
611 GetUntransformedScreenBounds(
603 DeathDomainBackdropTextureSystem.GetExpandedDomainDrawBounds(
612 604 out Vector2 destination,
613 605 out float destinationWidth,
614 606 out float destinationHeight);
@@ -711,11 +703,6 @@ internal class DeathDomainVisualSystem : ModSystem
711 703 rightEdges[band] = right;
712 704 }
713 705
714 Vector2 worldCenter = Main.screenPosition
715 + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
716 float referenceDiameter = DeathDomainBackdropTextureSystem.ReferenceDomainDiameter;
717 float horizontalScale = Main.screenWidth / referenceDiameter;
718 float verticalScale = Main.screenHeight / referenceDiameter;
719 706 float destinationRight = destination.X + destinationWidth;
720 707 float destinationBottom = destination.Y + destinationHeight;
721 708 for (int layer = 0; layer < 3; layer++)
@@ -727,7 +714,7 @@ internal class DeathDomainVisualSystem : ModSystem
727 714 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
728 715 };
729 716 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
730 GetBackdropSource(layer, texture, worldCenter, horizontalScale, verticalScale,
717 DeathDomainBackdropTextureSystem.GetExpandedDomainSourceFrame(layer,
731 718 out float sourceOffsetX, out float sourceOffsetY,
732 719 out float viewWidth, out float viewHeight);
733 720
@@ -769,11 +756,9 @@ internal class DeathDomainVisualSystem : ModSystem
769 756
770 757 private static void DrawFullScreenBackdrop(SpriteBatch batch, float mastery, float reveal)
771 758 {
772 Vector2 worldCenter = Main.screenPosition + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
773 float referenceDiameter = (160f + DeathNecklace.RadiusGrowthPerLevel * (DeathNecklace.MaxCoreLevel - 1)) * 2f;
774 float horizontalScale = Main.screenWidth / referenceDiameter;
775 float verticalScale = Main.screenHeight / referenceDiameter;
776 GetUntransformedScreenBounds(out Vector2 destination, out float destinationWidth, out float destinationHeight);
759 DeathDomainBackdropTextureSystem.GetExpandedDomainDrawBounds(
760 out Vector2 destination, out float destinationWidth,
761 out float destinationHeight);
777 762 for (int layer = 0; layer < 3; layer++)
778 763 {
779 764 float opacity = layer switch
@@ -783,7 +768,7 @@ internal class DeathDomainVisualSystem : ModSystem
783 768 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
784 769 };
785 770 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
786 GetBackdropSource(layer, texture, worldCenter, horizontalScale, verticalScale,
771 DeathDomainBackdropTextureSystem.GetExpandedDomainSourceFrame(layer,
787 772 out float sourceOffsetX, out float sourceOffsetY, out float viewWidth, out float viewHeight);
788 773
789 774 DrawBackdropBand(
@@ -1027,52 +1012,6 @@ internal class DeathDomainVisualSystem : ModSystem
1027 1012 return (float)(value - Math.Floor(value));
1028 1013 }
1029 1014
1030 private static void GetUntransformedScreenBounds(
1031 out Vector2 destination,
1032 out float destinationWidth,
1033 out float destinationHeight)
1034 {
1035 // This pass shares Terraria's active world SpriteBatch. Map the physical
1036 // viewport back through that batch's transform so the full-screen domain
1037 // still lands on the exact screen bounds at every game zoom. Taking all
1038 // four corners also keeps the viewport covered if another mod rotates or
1039 // shears the world view.
1040 Matrix inverse = Matrix.Invert(Main.GameViewMatrix.TransformationMatrix);
1041 Vector2 topLeft = Vector2.Transform(Vector2.Zero, inverse);
1042 Vector2 topRight = Vector2.Transform(new Vector2(Main.screenWidth, 0f), inverse);
1043 Vector2 bottomLeft = Vector2.Transform(new Vector2(0f, Main.screenHeight), inverse);
1044 Vector2 bottomRight = Vector2.Transform(new Vector2(Main.screenWidth, Main.screenHeight), inverse);
1045 float left = Math.Min(Math.Min(topLeft.X, topRight.X), Math.Min(bottomLeft.X, bottomRight.X));
1046 float right = Math.Max(Math.Max(topLeft.X, topRight.X), Math.Max(bottomLeft.X, bottomRight.X));
1047 float top = Math.Min(Math.Min(topLeft.Y, topRight.Y), Math.Min(bottomLeft.Y, bottomRight.Y));
1048 float bottom = Math.Max(Math.Max(topLeft.Y, topRight.Y), Math.Max(bottomLeft.Y, bottomRight.Y));
1049 destination = new Vector2(left, top);
1050 destinationWidth = Math.Max(1f, right - left);
1051 destinationHeight = Math.Max(1f, bottom - top);
1052 }
1053
1054 private static void GetBackdropSource(
1055 int layer,
1056 Texture2D texture,
1057 Vector2 worldCenter,
1058 float horizontalScale,
1059 float verticalScale,
1060 out float sourceOffsetX,
1061 out float sourceOffsetY,
1062 out float viewWidth,
1063 out float viewHeight)
1064 {
1065 _ = texture;
1066 DeathDomainBackdropTextureSystem.GetSourceFrame(layer, worldCenter,
1067 new Vector2(
1068 DeathDomainBackdropTextureSystem.ReferenceDomainDiameter
1069 * horizontalScale,
1070 DeathDomainBackdropTextureSystem.ReferenceDomainDiameter
1071 * verticalScale),
1072 out sourceOffsetX, out sourceOffsetY,
1073 out viewWidth, out viewHeight);
1074 }
1075
1076 1015 private static void DrawDomainRippleWaves(SpriteBatch batch, Vector2 center,
1077 1016 float radius, float time, float mastery, float pulse)
1078 1017 {