返回提交历史
Modified
Common/DeathDomainPrimitiveTextureSystem.cs
+113
-1
Modified
Common/DeathDomainVisualSystem.cs
+51
-60
Modified
Common/ReaperSkillVisualSystem.cs
+5
-5
Modified
Common/ReaperVfxDirector.cs
+17
-11
Modified
Common/ReaperVoidBackdropRenderer.cs
+45
-16
Modified
Projectiles/ReaperBloodScarProjectile.cs
+8
-5
Modified
Projectiles/SickleSwingProjectile.cs
+5
-1
XFEstudio/DeathMod
重构抗锯齿线条渲染,优化特效表现
重构视觉系统抗锯齿线条渲染,新增 SmoothStripMask 和 SmoothDiscMask 方法,替换 DrawSmoothLine 为 DrawSmoothStrip,提升带状抗锯齿连续性。优化黑洞、血痕、虚空裂隙等特效细节,调整颜色参数和分段数量,减少伪影。细化全屏闪光逻辑,避免多次触发时的过度闪烁,整体提升渲染质量与性能。
e405b1b
代码差异
7 个文件
+244
-99
@@ -17,6 +17,8 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
17
17
internal static Texture2D? BladeMask { get; private set; }
18
18
internal static Texture2D? RadialGradientMask { get; private set; }
19
19
internal static Texture2D? SmoothLineMask { get; private set; }
20
internal static Texture2D? SmoothStripMask { get; private set; }
21
internal static Texture2D? SmoothDiscMask { get; private set; }
20
22
21
23
private static Texture2D CreateBladeMask()
22
24
{
@@ -70,6 +72,28 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
70
72
return texture;
71
73
}
72
74
75
private static Texture2D CreateSmoothDiscMask()
76
{
77
const int size = 512;
78
Texture2D texture = new(Main.instance.GraphicsDevice, size, size, false, SurfaceFormat.Color);
79
Color[] pixels = new Color[size * size];
80
for (int y = 0; y < size; y++)
81
{
82
float normalizedY = (y + 0.5f) / size * 2f - 1f;
83
for (int x = 0; x < size; x++)
84
{
85
float normalizedX = (x + 0.5f) / size * 2f - 1f;
86
float distance = (float)Math.Sqrt(normalizedX * normalizedX + normalizedY * normalizedY);
87
float alpha = 1f - SmoothStep(0.982f, 1.006f, distance);
88
byte value = (byte)Math.Clamp((int)Math.Round(alpha * 255f), 0, 255);
89
pixels[y * size + x] = new Color(value, value, value, value);
90
}
91
}
92
93
texture.SetData(pixels);
94
return texture;
95
}
96
73
97
private static Texture2D CreateSmoothLineMask()
74
98
{
75
99
// The very wide aspect ratio keeps the antialiased end caps compact even
@@ -105,22 +129,54 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
105
129
return texture;
106
130
}
107
131
132
private static Texture2D CreateSmoothStripMask()
133
{
134
// Connected curves must not use the rounded end caps from SmoothLineMask:
135
// dozens of overlapping caps make every sampling point visibly brighter.
136
// This mask is fully continuous along X and only antialiases its two long
137
// edges, allowing short rotated spans to read as one uninterrupted ribbon.
138
const int width = 256;
139
const int height = 96;
140
Texture2D texture = new(Main.instance.GraphicsDevice, width, height, false, SurfaceFormat.Color);
141
Color[] pixels = new Color[width * height];
142
float halfHeight = height * 0.5f - 1f;
143
for (int y = 0; y < height; y++)
144
{
145
float distance = Math.Abs(y + 0.5f - height * 0.5f);
146
float alpha = 1f - SmoothStep(halfHeight - 2.25f, halfHeight + 0.75f, distance);
147
byte value = (byte)Math.Clamp((int)Math.Round(alpha * 255f), 0, 255);
148
Color pixel = new(value, value, value, value);
149
for (int x = 0; x < width; x++)
150
pixels[y * width + x] = pixel;
151
}
152
153
texture.SetData(pixels);
154
return texture;
155
}
156
108
157
public override void Unload()
109
158
{
110
159
Texture2D? bladeTexture = BladeMask;
111
160
Texture2D? gradientTexture = RadialGradientMask;
112
161
Texture2D? smoothLineTexture = SmoothLineMask;
162
Texture2D? smoothStripTexture = SmoothStripMask;
163
Texture2D? smoothDiscTexture = SmoothDiscMask;
113
164
BladeMask = null;
114
165
RadialGradientMask = null;
115
166
SmoothLineMask = null;
167
SmoothStripMask = null;
168
SmoothDiscMask = null;
116
169
if (!Main.dedServ && (bladeTexture is not null || gradientTexture is not null
117
|| smoothLineTexture is not null))
170
|| smoothLineTexture is not null || smoothStripTexture is not null
171
|| smoothDiscTexture is not null))
118
172
{
119
173
Main.QueueMainThreadAction(() =>
120
174
{
121
175
bladeTexture?.Dispose();
122
176
gradientTexture?.Dispose();
123
177
smoothLineTexture?.Dispose();
178
smoothStripTexture?.Dispose();
179
smoothDiscTexture?.Dispose();
124
180
});
125
181
}
126
182
}
@@ -192,6 +248,16 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
192
248
0f);
193
249
}
194
250
251
internal static void DrawSmoothDisc(SpriteBatch batch, Vector2 center, float radius, Color color)
252
{
253
if (Main.dedServ || radius <= 0.5f)
254
return;
255
256
Texture2D texture = SmoothDiscMask ??= CreateSmoothDiscMask();
257
batch.Draw(texture, center, null, color, 0f, texture.Size() * 0.5f,
258
radius * 2f / texture.Width, SpriteEffects.None, 0f);
259
}
260
195
261
internal static void DrawSmoothLine(
196
262
SpriteBatch batch,
197
263
Vector2 start,
@@ -230,6 +296,52 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
230
296
end - Main.screenPosition, color, width);
231
297
}
232
298
299
internal static void DrawSmoothStrip(
300
SpriteBatch batch,
301
Vector2 start,
302
Vector2 end,
303
Color color,
304
float width)
305
{
306
if (Main.dedServ || width <= 0.05f)
307
return;
308
309
Vector2 delta = end - start;
310
float length = delta.Length();
311
if (length <= 0.1f)
312
return;
313
314
Texture2D texture = SmoothStripMask ??= CreateSmoothStripMask();
315
Vector2 direction = delta / length;
316
// A sub-pixel overlap closes rotated joins without reintroducing the
317
// conspicuous circular stamps produced by the capped line mask.
318
float overlap = Math.Min(0.7f, width * 0.08f);
319
start -= direction * overlap;
320
end += direction * overlap;
321
delta = end - start;
322
length = delta.Length();
323
batch.Draw(
324
texture,
325
(start + end) * 0.5f,
326
null,
327
color,
328
delta.ToRotation(),
329
texture.Size() * 0.5f,
330
new Vector2(length / texture.Width, width / texture.Height),
331
SpriteEffects.None,
332
0f);
333
}
334
335
internal static void DrawSmoothWorldStrip(
336
Vector2 start,
337
Vector2 end,
338
Color color,
339
float width)
340
{
341
DrawSmoothStrip(Main.spriteBatch, start - Main.screenPosition,
342
end - Main.screenPosition, color, width);
343
}
344
233
345
private static float SmoothStep(float start, float end, float value)
234
346
{
235
347
if (end <= start)
@@ -94,7 +94,7 @@ internal class DeathDomainVisualSystem : ModSystem
94
94
batch.Begin(
95
95
SpriteSortMode.Deferred,
96
96
BlendState.AlphaBlend,
97
SamplerState.PointClamp,
97
SamplerState.LinearClamp,
98
98
DepthStencilState.None,
99
99
RasterizerState.CullNone,
100
100
null,
@@ -193,13 +193,14 @@ internal class DeathDomainVisualSystem : ModSystem
193
193
mastery,
194
194
reveal);
195
195
}
196
DrawFilledNoiseDisc(
196
// The old scan-band fill stamped hundreds of horizontal rounded strips
197
// over the centre and produced the visible television-line pattern. A
198
// single antialiased disc supplies the same restrained shadow without any
199
// row seams; the animated noisy boundary still defines its organic edge.
200
DeathDomainPrimitiveTextureSystem.DrawSmoothDisc(
197
201
batch,
198
202
center,
199
203
coreRadius,
200
coreNoiseTime,
201
coreNoiseSeed,
202
coreNoiseAmplitude,
203
204
new Color(3, 0, 10) * (MathHelper.Lerp(0.08f, 0.26f, mastery) * reveal));
204
205
DeathDomainPrimitiveTextureSystem.DrawRadialGradient(
205
206
batch,
@@ -649,27 +650,8 @@ internal class DeathDomainVisualSystem : ModSystem
649
650
if (radius <= 1f || opacity <= 0.001f)
650
651
return;
651
652
652
float verticalScale = MathHelper.Lerp(1f, 0.16f, collapse);
653
DeathDomainPrimitiveTextureSystem.DrawRadialGradient(
654
batch, center, radius * 1.62f, new Color(0, 0, 3) * (opacity * 0.86f));
655
DrawFilledEllipse(batch, center, radius * 0.88f,
656
Math.Max(1f, radius * 0.38f * verticalScale), Color.Black * opacity);
657
DrawBlackHoleSuctionStreams(batch, center, radius, verticalScale,
658
rotation, opacity, seed, large: false);
659
DrawEllipseArc(batch, center, radius * 1.34f, radius * 0.70f * verticalScale,
660
rotation, -2.65f, 2.2f, 30, new Color(176, 28, 255) * (opacity * 0.72f), 2.1f);
661
DrawEllipseArc(batch, center, radius * 1.62f, radius * 0.46f * verticalScale,
662
-rotation * 0.7f, 0.18f, 4.45f, 34, new Color(255, 28, 78) * (opacity * 0.82f), 1.35f);
663
for (int particle = 0; particle < 3; particle++)
664
{
665
float angle = rotation * (particle % 2 == 0 ? 1f : -0.8f)
666
+ particle * MathHelper.TwoPi / 3f + Hash01(seed + particle * 17) * 0.7f;
667
Vector2 position = center + new Vector2(
668
(float)Math.Cos(angle) * radius * 1.72f,
669
(float)Math.Sin(angle) * radius * 0.58f * verticalScale);
670
DrawCircle(batch, position, Math.Max(0.8f, radius * 0.055f), 8,
671
new Color(255, 104, 145) * opacity, 1.2f, angle);
672
}
653
DrawBackdropDomainBlackHole(batch, center, radius, rotation, opacity,
654
collapse, 0.34f, seed, large: false);
673
655
}
674
656
675
657
private static void DrawLargeBackdropBlackHole(
@@ -685,34 +667,43 @@ internal class DeathDomainVisualSystem : ModSystem
685
667
if (radius <= 2f || opacity <= 0.001f)
686
668
return;
687
669
688
float verticalScale = MathHelper.Lerp(1f, 0.10f, collapse);
689
float coreX = radius * 1.18f;
690
float coreY = radius * 0.52f * verticalScale;
691
DeathDomainPrimitiveTextureSystem.DrawRadialGradient(
692
batch, center, radius * 2.25f, new Color(8, 0, 19) * (opacity * 0.68f));
693
DrawFilledEllipse(batch, center, coreX, Math.Max(1.2f, coreY),
670
DrawBackdropDomainBlackHole(batch, center, radius, rotation, opacity,
671
collapse, mastery, seed, large: true);
672
}
673
674
private static void DrawBackdropDomainBlackHole(
675
SpriteBatch batch,
676
Vector2 center,
677
float radius,
678
float rotation,
679
float opacity,
680
float collapse,
681
float mastery,
682
int seed,
683
bool large)
684
{
685
float time = Main.GlobalTimeWrappedHourly;
686
float coreRadius = radius * (large ? 0.68f : 0.62f);
687
float pulse = opacity * (0.88f + (float)Math.Sin(time * 2.7f + seed) * 0.08f);
688
DeathDomainPrimitiveTextureSystem.DrawRadialGradient(batch, center,
689
radius * (large ? 1.95f : 1.65f), new Color(42, 0, 16) * (opacity * 0.42f));
690
DrawBlackHoleSuctionStreams(batch, center, radius, 1f, rotation,
691
opacity * (1f - collapse * 0.38f), seed, large);
692
693
// Five inward-curving arms deliberately mirror the player's event
694
// horizon instead of the old unrelated crossed-orbit atom symbol.
695
for (int arm = 0; arm < 5; arm++)
696
DrawAccretionSpiral(batch, center, coreRadius * 0.96f, radius * 1.06f,
697
time + rotation * 0.72f, arm, mastery, pulse);
698
699
DeathDomainPrimitiveTextureSystem.DrawSmoothDisc(batch, center, coreRadius,
694
700
new Color(0, 0, 2) * (opacity * 0.98f));
695
DrawBlackHoleSuctionStreams(batch, center, radius, verticalScale,
696
rotation, opacity, seed, large: true);
697
698
Color outer = Color.Lerp(new Color(116, 18, 215), new Color(225, 22, 82), mastery);
699
Color hot = Color.Lerp(new Color(238, 60, 166), new Color(255, 116, 82), mastery);
700
DrawEllipseArc(batch, center, radius * 2.12f, radius * 0.67f * verticalScale,
701
rotation, -2.92f, 2.48f, 54, outer * (opacity * 0.62f), 3.2f);
702
DrawEllipseArc(batch, center, radius * 1.72f, radius * 0.46f * verticalScale,
703
rotation + 0.08f, 0.05f, 3.12f, 44, hot * (opacity * 0.94f), 2.4f);
704
DrawEllipseArc(batch, center, radius * 2.52f, radius * 0.92f * verticalScale,
705
-rotation * 0.35f, -0.72f, 1.48f, 34, new Color(122, 86, 255) * (opacity * 0.42f), 1.3f);
706
707
// Offset lensing crescents make the large variant read as warped space,
708
// not as a scaled copy of the player's five-arm circular domain.
709
Vector2 lensOffset = rotation.ToRotationVector2() * radius * 0.19f;
710
DrawEllipseArc(batch, center + lensOffset, radius * 1.36f,
711
radius * 0.24f * verticalScale, rotation, -2.75f, -0.25f, 28,
712
new Color(255, 172, 196) * (opacity * 0.58f), 1.15f);
713
DrawEllipseArc(batch, center - lensOffset, radius * 1.36f,
714
radius * 0.24f * verticalScale, rotation, 0.36f, 2.75f, 28,
715
outer * (opacity * 0.46f), 1.05f);
701
DrawNoiseBoundary(batch, center, coreRadius + radius * 0.07f,
702
time * 0.76f + rotation, seed * 0.013f, radius * 0.07f,
703
large ? 3.2f : 2.1f, opacity * 0.34f, mastery, gaps: true);
704
DrawNoiseBoundary(batch, center, coreRadius,
705
-time * 0.94f + rotation, seed * 0.021f, radius * 0.045f,
706
large ? 2.4f : 1.55f, opacity * 0.92f, mastery, gaps: false);
716
707
}
717
708
718
709
private static void DrawFilledEllipse(
@@ -835,7 +826,7 @@ internal class DeathDomainVisualSystem : ModSystem
835
826
float ripple = (float)Math.Sin(angle * 9f - time * 2.1f + wave * 1.7f) * 2.6f
836
827
+ (float)Math.Sin(angle * 17f + time * 1.3f) * 0.9f;
837
828
Vector2 current = center + angle.ToRotationVector2() * (waveRadius + ripple);
838
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, previous, current,
829
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, previous, current,
839
830
Color.Lerp(new Color(122, 4, 34), new Color(255, 54, 92), mastery)
840
831
* opacity,
841
832
MathHelper.Lerp(1.1f, 2.4f, 1f - travel));
@@ -868,7 +859,7 @@ internal class DeathDomainVisualSystem : ModSystem
868
859
localRadius += (float)Math.Sin(localAngle * 5f + seed * 0.07f)
869
860
* radius * 0.025f * (1f - inward);
870
861
Vector2 local = new((float)Math.Cos(localAngle) * localRadius,
871
(float)Math.Sin(localAngle) * localRadius * verticalScale * 0.52f);
862
(float)Math.Sin(localAngle) * localRadius * verticalScale);
872
863
Vector2 current = center + local.RotatedBy(rotation);
873
864
if (point > 0)
874
865
{
@@ -879,12 +870,12 @@ internal class DeathDomainVisualSystem : ModSystem
879
870
// the far half remains visible behind the black core, giving
880
871
// the accretion flow genuine front/back depth.
881
872
float nearSide = 0.55f + 0.45f * Math.Max(0f, (float)Math.Sin(localAngle));
882
Color color = Color.Lerp(new Color(80, 8, 142),
883
new Color(255, 30, 76), inward * 0.82f + glow * 0.18f);
873
Color color = Color.Lerp(new Color(58, 0, 12),
874
new Color(255, 42, 78), inward * 0.82f + glow * 0.18f);
884
875
float streamOpacity = opacity * body * nearSide * (0.18f + glow * 0.62f);
885
876
float width = MathHelper.Lerp(0.65f, large ? 3.6f : 2.2f, inward)
886
877
* (0.72f + glow * 0.38f);
887
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, previous,
878
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, previous,
888
879
current, color * streamOpacity, width);
889
880
}
890
881
previous = current;
@@ -1128,14 +1119,14 @@ internal class DeathDomainVisualSystem : ModSystem
1128
1119
{
1129
1120
float angle = rotation + MathHelper.TwoPi * index / segments;
1130
1121
Vector2 current = center + angle.ToRotationVector2() * radius;
1131
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, previous, current, color, width);
1122
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, previous, current, color, width);
1132
1123
previous = current;
1133
1124
}
1134
1125
}
1135
1126
1136
1127
private static void DrawSegment(SpriteBatch batch, Vector2 start, Vector2 segment, Color color, float width)
1137
1128
{
1138
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch,
1129
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch,
1139
1130
start, start + segment, color, width);
1140
1131
}
1141
1132
@@ -122,12 +122,12 @@ internal sealed class ReaperSkillVisualSystem : ModSystem
122
122
if (IsNearScreen(center, 620f))
123
123
PlaySkillSound(form, skill, level, variant, center);
124
124
125
// Unique skills keep their restrained camera punctuation, but never tint
126
// the entire viewport. A three-frame pale impact used to present as a
127
// one-frame white screen when several automatic triggers were merged.
125
128
if (level >= 3 && IsNearScreen(center))
126
{
127
Color impact = ReaperCombatRegistry.GetSecondaryColor(form);
128
ReaperVfxDirector.TriggerGlobalImpact(direction, 2.8f, 6, impact,
129
variant > 0 ? 0.14f : 0.09f, 3, 0.06f);
130
}
129
ReaperVfxDirector.TriggerGlobalImpact(direction, 2.8f, 6,
130
Color.Transparent, 0f, 0, 0.06f);
131
131
}
132
132
133
133
public override void OnWorldUnload() => Visuals.Clear();
@@ -163,18 +163,24 @@ public sealed class ReaperVfxDirector : ModSystem
163
163
shakeDirection = (shakeDirection + direction * 0.45f).SafeNormalize(shakeDirection);
164
164
}
165
165
166
if (opacity >= flashOpacity || flashFrames <= 1)
166
// A camera-only impact must not create, extend or recolor an unrelated
167
// full-screen flash that is already active (for example an overlapping
168
// ultimate). Unique-skill cues intentionally pass zero here.
169
if (opacity > 0.001f && colorFrames > 0)
167
170
{
168
flashColor = color;
169
flashOpacity = opacity;
170
flashDuration = Math.Max(1, colorFrames);
171
flashFrames = colorFrames;
172
}
173
else
174
{
175
flashFrames = Math.Max(flashFrames, colorFrames);
176
flashDuration = Math.Max(flashDuration, flashFrames);
177
flashColor = Color.Lerp(flashColor, color, 0.25f);
171
if (opacity >= flashOpacity || flashFrames <= 1)
172
{
173
flashColor = color;
174
flashOpacity = opacity;
175
flashDuration = Math.Max(1, colorFrames);
176
flashFrames = colorFrames;
177
}
178
else
179
{
180
flashFrames = Math.Max(flashFrames, colorFrames);
181
flashDuration = Math.Max(flashDuration, flashFrames);
182
flashColor = Color.Lerp(flashColor, color, 0.25f);
183
}
178
184
}
179
185
180
186
edgeDarkness = Math.Max(edgeDarkness, MathHelper.Clamp(vignette, 0f, 0.62f));
@@ -33,14 +33,14 @@ internal static class ReaperVoidBackdropRenderer
33
33
{
34
34
Vector2 screenStart = start - Main.screenPosition;
35
35
Vector2 screenEnd = end - Main.screenPosition;
36
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, screenStart, screenEnd,
37
new Color(92, 12, 155, 0) * (opacity * 0.34f), width + 5f);
38
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, screenStart, screenEnd,
39
new Color(132, 32, 210, 205) * (opacity * 0.82f), width + 2f);
40
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, screenStart, screenEnd,
36
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, screenStart, screenEnd,
37
new Color(58, 4, 92, 0) * (opacity * 0.26f), width + 4f);
38
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, screenStart, screenEnd,
39
new Color(112, 26, 181, 220) * (opacity * 0.68f), width + 1.7f);
40
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, screenStart, screenEnd,
41
41
new Color(2, 0, 6, 252) * opacity, width);
42
DeathDomainPrimitiveTextureSystem.DrawSmoothLine(batch, screenStart, screenEnd,
43
new Color(10, 2, 18, 205) * (opacity * 0.68f), Math.Max(1f, width * 0.48f));
42
DeathDomainPrimitiveTextureSystem.DrawSmoothStrip(batch, screenStart, screenEnd,
43
new Color(9, 2, 16, 220) * (opacity * 0.72f), Math.Max(1f, width * 0.46f));
44
44
}
45
45
46
46
internal static void DrawHitRift(SpriteBatch batch, Vector2 center, Vector2 axis,
@@ -120,17 +120,18 @@ internal static class ReaperVoidBackdropRenderer
120
120
if (u > visibleEnd)
121
121
continue;
122
122
123
Vector2 root = GetRiftCenter(center, axis, normal, u, width, seed)
124
+ axis * (u * length * 0.5f);
125
123
float side = ((branch + seed) & 1) == 0 ? 1f : -1f;
124
float halfWidth = GetRiftHalfWidth(u, width, seed);
125
Vector2 root = GetRiftCenter(center, axis, normal, u, width, seed)
126
+ axis * (u * length * 0.5f)
127
+ normal * side * halfWidth * 0.82f;
126
128
float branchLength = MathHelper.Clamp(width * (1.45f + Hash01(seed + branch * 31) * 1.3f),
127
129
8f, 72f);
128
130
Vector2 direction = (normal * side + axis * (Hash01(seed + branch * 43) - 0.5f) * 0.85f)
129
131
.SafeNormalize(normal * side);
130
Vector2 middle = root + direction.RotatedBy(-side * 0.18f) * branchLength * 0.58f;
131
Vector2 tip = middle + direction.RotatedBy(side * 0.34f) * branchLength * 0.42f;
132
DrawCoreStrip(batch, root, middle, Math.Max(0.8f, width * 0.19f), opacity * 0.72f);
133
DrawCoreStrip(batch, middle, tip, Math.Max(0.55f, width * 0.10f), opacity * 0.48f);
132
DrawBranchPath(batch, root, direction, branchLength,
133
Math.Max(0.9f, width * 0.17f), opacity * 0.68f,
134
seed + branch * 173);
134
135
}
135
136
}
136
137
@@ -141,13 +142,41 @@ internal static class ReaperVoidBackdropRenderer
141
142
if (delta.LengthSquared() < 9f)
142
143
return;
143
144
Vector2 axis = delta.SafeNormalize(Vector2.UnitX);
145
Vector2 normal = axis.RotatedBy(MathHelper.PiOver2);
144
146
float side = ((seed + index) & 1) == 0 ? 1f : -1f;
145
Vector2 root = Vector2.Lerp(start, end, 0.58f);
147
Vector2 root = Vector2.Lerp(start, end, 0.58f)
148
+ normal * side * width * 0.47f;
146
149
Vector2 direction = axis.RotatedBy(side * MathHelper.Lerp(0.55f, 0.92f,
147
150
Hash01(seed + index * 79)));
148
151
float length = MathHelper.Clamp(width * 1.7f, 7f, 29f);
149
Vector2 tip = root + direction * length;
150
DrawCoreStrip(batch, root, tip, Math.Max(0.55f, width * 0.14f), opacity * 0.52f);
152
DrawBranchPath(batch, root, direction, length,
153
Math.Max(0.65f, width * 0.13f), opacity * 0.50f,
154
seed + index * 211);
155
}
156
157
private static void DrawBranchPath(SpriteBatch batch, Vector2 root,
158
Vector2 direction, float length, float width, float opacity, int seed)
159
{
160
direction = direction.SafeNormalize(Vector2.UnitY);
161
Vector2 normal = direction.RotatedBy(MathHelper.PiOver2);
162
int samples = Math.Clamp((int)Math.Ceiling(length / 5f), 5, 15);
163
float wavePhase = Hash01(seed + 19) * MathHelper.TwoPi;
164
Vector2 previous = root;
165
for (int sample = 1; sample <= samples; sample++)
166
{
167
float progress = sample / (float)samples;
168
float envelope = (float)Math.Sin(progress * MathHelper.Pi);
169
float wave = ((float)Math.Sin(progress * 5.4f + wavePhase)
170
+ (float)Math.Sin(progress * 11.1f - wavePhase * 0.7f) * 0.26f)
171
* length * 0.055f * envelope;
172
Vector2 current = root + direction * (length * progress) + normal * wave;
173
float localWidth = Math.Max(0.45f,
174
width * (float)Math.Pow(1f - progress * 0.86f, 0.72f));
175
DrawCoreStrip(batch, previous, current, localWidth, opacity * (1f - progress * 0.32f));
176
DrawStarsInStrip(batch, previous, current, localWidth,
177
opacity * (1f - progress * 0.42f));
178
previous = current;
179
}
151
180
}
152
181
153
182
private static float Hash01(int seed)
@@ -102,7 +102,10 @@ public sealed class ReaperBloodScarProjectile : ModProjectile
102
102
float fade = Smooth01(Projectile.timeLeft / 34f);
103
103
Vector2 axis = Projectile.velocity.SafeNormalize(Vector2.UnitX);
104
104
Vector2 normal = axis.RotatedBy(MathHelper.PiOver2);
105
const int segments = 34;
105
// Long ultimate scars used to expose their polygonal sampling (up to
106
// seventy pixels per span). Keep each span short enough that the same
107
// continuous wave noise reads as a smooth torn edge at every length.
108
int segments = Math.Clamp((int)Math.Ceiling(scarLength / 16f), 48, 160);
106
109
float visibleEnd = MathHelper.Lerp(-1f, 1f, reveal);
107
110
Vector2 previous = GetPoint(-1f);
108
111
for (int segment = 1; segment <= segments; segment++)
@@ -187,13 +190,13 @@ public sealed class ReaperBloodScarProjectile : ModProjectile
187
190
private static void DrawScarSegment(Vector2 start, Vector2 end, float width,
188
191
float opacity)
189
192
{
190
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
193
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldStrip(start, end,
191
194
new Color(112, 0, 20, 0) * (opacity * 0.40f), width + 8f);
192
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
195
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldStrip(start, end,
193
196
new Color(246, 22, 56, 235) * (opacity * 0.92f), width + 3f);
194
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
197
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldStrip(start, end,
195
198
new Color(8, 0, 3, 252) * opacity, width);
196
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
199
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldStrip(start, end,
197
200
new Color(27, 0, 8, 235) * opacity, Math.Max(1f, width * 0.54f));
198
201
}
199
202
@@ -284,7 +284,11 @@ public sealed class SickleSwingProjectile : ModProjectile
284
284
return false;
285
285
DrawFormBackdrop();
286
286
DrawTrail();
287
if (HasStageVisual(ReaperStage.StageII))
287
// Blood's chevron rune read as an aiming arrow, while Void's parallel
288
// rune bars visibly crossed the torn background. Their trail surfaces are
289
// already the form language, so do not stamp unrelated glyphs over them.
290
if (HasStageVisual(ReaperStage.StageII)
291
&& VisualForm is not ReaperFormId.Blood and not ReaperFormId.Void)
288
292
DrawStageRunes();
289
293
Texture2D texture = ModContent.Request<Texture2D>(
290
294
ReaperCombatRegistry.GetTexturePath(snapshot.Form, snapshot.Stage)).Value;