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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

统一领域背景层并重构镰刀拖尾

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

代码差异

8 个文件 +360 -124
Modified Common/DeathDomainBackdropTextureSystem.cs +15 -11
@@ -14,8 +14,8 @@ namespace DeathMod.Common;
14 14 [Autoload(Side = ModSide.Client)]
15 15 internal sealed class DeathDomainBackdropTextureSystem : ModSystem
16 16 {
17 internal const int LayerWidth = 1024;
18 internal const int LayerHeight = 512;
17 internal const int LayerWidth = 4096;
18 internal const int LayerHeight = 1024;
19 19
20 20 private static readonly Texture2D?[] layers = new Texture2D?[3];
21 21
@@ -48,7 +48,10 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
48 48 float v = (y + 0.5f) / LayerHeight;
49 49 for (int x = 0; x < LayerWidth; x++)
50 50 {
51 float u = (x + 0.5f) / LayerWidth;
51 // Keep roughly the same feature scale as the original 1024-wide
52 // plate while supplying enough unique scenery for a 4K full-screen
53 // view at exactly the same scale as the player's circular window.
54 float u = (x + 0.5f) / 1024f;
52 55 pixels[y * LayerWidth + x] = layer switch
53 56 {
54 57 0 => BuildFarPixel(x, y, u, v),
@@ -70,7 +73,8 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
70 73
71 74 float crimsonCloud = Wave(u, v, 2f, 1f, 0.13f)
72 75 + Wave(u, v, 5f, -2f, 1.7f) * 0.45f
73 + Wave(u, v, 9f, 3f, 4.2f) * 0.2f;
76 + Wave(u, v, 9f, 3f, 4.2f) * 0.2f
77 + Wave(u, v, 0.37f, 0.45f, 2.3f) * 0.24f;
74 78 crimsonCloud = Smooth01((crimsonCloud + 1.1f) / 2.45f);
75 79 float violetCloud = Smooth01((Wave(u, v, 3f, -2f, 3.4f) + 0.78f) / 1.78f);
76 80 color += new Vector3(0.16f, 0.006f, 0.055f) * crimsonCloud * (0.32f + v * 0.34f);
@@ -90,7 +94,9 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
90 94 // animate on their own: parallax reveals them like distant landmarks while
91 95 // the nearer mountains and blood mist intermittently hide them again.
92 96 ApplyDistantBlackHole(ref color, u, v, 0.18f, 0.27f, 0.034f, 0.22f, 1.7f);
93 ApplyDistantBlackHole(ref color, u, v, 0.79f, 0.64f, 0.022f, -0.38f, 4.9f);
97 ApplyDistantBlackHole(ref color, u, v, 1.43f, 0.64f, 0.022f, -0.38f, 4.9f);
98 ApplyDistantBlackHole(ref color, u, v, 2.72f, 0.36f, 0.029f, 0.31f, 7.3f);
99 ApplyDistantBlackHole(ref color, u, v, 3.61f, 0.72f, 0.019f, -0.17f, 9.8f);
94 100
95 101 return Opaque(color);
96 102 }
@@ -106,10 +112,6 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
106 112 float seed)
107 113 {
108 114 float dx = u - centerU;
109 if (dx > 0.5f)
110 dx -= 1f;
111 else if (dx < -0.5f)
112 dx += 1f;
113 115 float dy = v - centerV;
114 116 float reach = radius * 3.35f;
115 117 if (Math.Abs(dx) > reach || Math.Abs(dy) > reach)
@@ -181,7 +183,8 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
181 183 // Two ranges of dead mountains create the mid-distance depth plane.
182 184 float rearHorizon = 0.59f
183 185 + (float)Math.Sin(u * MathHelper.TwoPi * 3f + 0.4f) * 0.06f
184 + (float)Math.Sin(u * MathHelper.TwoPi * 7f) * 0.025f;
186 + (float)Math.Sin(u * MathHelper.TwoPi * 7f) * 0.025f
187 + (float)Math.Sin(u * MathHelper.TwoPi * 0.31f + 1.4f) * 0.024f;
185 188 if (v > rearHorizon)
186 189 {
187 190 float depth = Smooth01((v - rearHorizon) / 0.18f);
@@ -201,7 +204,8 @@ internal sealed class DeathDomainBackdropTextureSystem : ModSystem
201 204 {
202 205 float broadMist = Wave(u, v, 2f, 1f, 0.35f)
203 206 + Wave(u, v, 4f, -2f, 2.2f) * 0.52f
204 + Wave(u, v, 7f, 3f, 4.6f) * 0.24f;
207 + Wave(u, v, 7f, 3f, 4.6f) * 0.24f
208 + Wave(u, v, 0.43f, -0.38f, 5.1f) * 0.26f;
205 209 float curledMist = Wave(u, v, 3f, -3f, 1.1f)
206 210 + Wave(u, v, 9f, 4f, 3.7f) * 0.34f;
207 211 float body = Smooth01((broadMist + 0.72f) / 2.2f);
Modified Common/DeathDomainVisualSystem.cs +189 -39
@@ -19,6 +19,16 @@ internal class DeathDomainVisualSystem : ModSystem
19 19 private readonly int[] cachedMastery = new int[Main.maxPlayers];
20 20 private readonly bool[] cachedFullScreen = new bool[Main.maxPlayers];
21 21
22 public override void Load()
23 {
24 On_Main.DrawBackground += DrawDomainBackdropsAfterVanillaBackground;
25 }
26
27 public override void Unload()
28 {
29 On_Main.DrawBackground -= DrawDomainBackdropsAfterVanillaBackground;
30 }
31
22 32 public override void PostUpdateEverything()
23 33 {
24 34 if (Main.gameMenu)
@@ -113,14 +123,21 @@ internal class DeathDomainVisualSystem : ModSystem
113 123 continue;
114 124 }
115 125 if (!fullScreen)
116 DrawDomainRing(batch, pixel, screenCenter, radius, mastery, animation);
126 DrawDomainRing(batch, pixel, screenCenter, radius, mastery, animation, drawBackdrop: false);
117 127 else
118 128 DrawFullScreenDomain(batch, pixel, screenCenter, radius, mastery, animation);
119 129 }
120 130 batch.End();
121 131 }
122 132
123 private static void DrawDomainRing(SpriteBatch batch, Texture2D pixel, Vector2 center, float targetRadius, int mastery, float animation)
133 private static void DrawDomainRing(
134 SpriteBatch batch,
135 Texture2D pixel,
136 Vector2 center,
137 float targetRadius,
138 int mastery,
139 float animation,
140 bool drawBackdrop = true)
124 141 {
125 142 float time = Main.GlobalTimeWrappedHourly;
126 143 float reveal = MathHelper.SmoothStep(0f, 1f, animation);
@@ -135,14 +152,23 @@ internal class DeathDomainVisualSystem : ModSystem
135 152 float radius = MathHelper.Lerp(8f, targetRadius, expansion) * overshoot;
136 153 float pulse = (0.82f + (float)Math.Sin(time * 3.2f) * 0.12f) * manifestedReveal;
137 154 float masteryProgress = GetMasteryProgress(mastery);
138 DrawBlackHoleField(batch, pixel, center, radius, masteryProgress, pulse, time, manifestedReveal);
155 DrawBlackHoleField(batch, pixel, center, radius, masteryProgress, pulse, time, manifestedReveal, drawBackdrop);
139 156 DrawNoiseBoundary(batch, pixel, center, radius + 3f, time, 0.4f, 12f, 9f, 0.18f * pulse, masteryProgress, gaps: true);
140 157 DrawNoiseBoundary(batch, pixel, center, radius, time, 1.7f, 9f, 5f, 0.42f * pulse, masteryProgress, gaps: true);
141 158 DrawNoiseBoundary(batch, pixel, center, radius - 2f, time, 3.3f, 7f, 2.7f + masteryProgress, 0.94f * pulse, masteryProgress, gaps: false);
142 159 DrawNoiseBoundary(batch, pixel, center, radius - 7f, -time, 5.1f, 5f, 1.4f, 0.58f * pulse, masteryProgress, gaps: true);
143 160 }
144 161
145 private static void DrawBlackHoleField(SpriteBatch batch, Texture2D pixel, Vector2 center, float radius, float mastery, float pulse, float time, float reveal)
162 private static void DrawBlackHoleField(
163 SpriteBatch batch,
164 Texture2D pixel,
165 Vector2 center,
166 float radius,
167 float mastery,
168 float pulse,
169 float time,
170 float reveal,
171 bool drawBackdrop)
146 172 {
147 173 float coreGap = MathHelper.Lerp(22f, 14f, mastery);
148 174 float coreRadius = radius > coreGap + 6f ? radius - coreGap : radius * 0.58f;
@@ -157,15 +183,18 @@ internal class DeathDomainVisualSystem : ModSystem
157 183 float coreNoiseTime = time * 0.8f;
158 184 const float coreNoiseSeed = 16.2f;
159 185 float coreNoiseAmplitude = Math.Min(coreRadius * 0.18f, MathHelper.Lerp(5f, 10f, mastery) * MathHelper.Lerp(0.35f, 1f, reveal));
160 DrawClippedDeathDomainBackdrop(
161 batch,
162 center,
163 coreRadius,
164 coreNoiseTime,
165 coreNoiseSeed,
166 coreNoiseAmplitude,
167 mastery,
168 reveal);
186 if (drawBackdrop)
187 {
188 DrawClippedDeathDomainBackdrop(
189 batch,
190 center,
191 coreRadius,
192 coreNoiseTime,
193 coreNoiseSeed,
194 coreNoiseAmplitude,
195 mastery,
196 reveal);
197 }
169 198 DrawFilledNoiseDisc(
170 199 batch,
171 200 pixel,
@@ -215,9 +244,6 @@ internal class DeathDomainVisualSystem : ModSystem
215 244 // only three texture batches instead of switching textures for every row.
216 245 for (int layer = 0; layer < 3; layer++)
217 246 {
218 float parallax = layer switch { 0 => 0.07f, 1 => 0.18f, _ => 0.34f };
219 float horizontalViewFraction = layer switch { 0 => 0.68f, 1 => 0.9f, _ => 0.84f };
220 float verticalViewFraction = layer switch { 0 => 0.78f, 1 => 0.9f, _ => 0.86f };
221 247 float opacity = layer switch
222 248 {
223 249 0 => MathHelper.Lerp(0.88f, 1f, mastery) * reveal,
@@ -225,15 +251,8 @@ internal class DeathDomainVisualSystem : ModSystem
225 251 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
226 252 };
227 253 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
228 float viewWidth = texture.Width * horizontalViewFraction;
229 float viewHeight = texture.Height * verticalViewFraction;
230 float sourceOffsetX = PositiveModulo(worldCenter.X * parallax, texture.Width);
231 // Vertical sampling traverses the source image only once over the full
232 // world depth. It never wraps across the top/bottom texture edge, so a
233 // second horizon or mountain range cannot be spliced into the domain.
234 float worldHeight = Math.Max(1f, Main.maxTilesY * 16f);
235 float worldDepth = MathHelper.Clamp(worldCenter.Y / worldHeight, 0f, 1f);
236 float sourceOffsetY = worldDepth * Math.Max(0f, texture.Height - viewHeight);
254 GetBackdropSource(layer, texture, worldCenter, 1f, 1f, out float sourceOffsetX,
255 out float sourceOffsetY, out float viewWidth, out float viewHeight);
237 256
238 257 for (int index = 0; index < bands; index++)
239 258 {
@@ -358,24 +377,133 @@ internal class DeathDomainVisualSystem : ModSystem
358 377 int mastery,
359 378 float animation)
360 379 {
361 float reveal = MathHelper.SmoothStep(0f, 1f, animation);
362 float masteryProgress = GetMasteryProgress(mastery);
363 DrawFullScreenBackdrop(batch, masteryProgress, reveal);
364
365 380 // Full-screen mastery removes the circular crop from the domain scene. The
366 381 // player's event horizon is still the same max-level field, rather than a
367 382 // circle enlarged until it reaches the viewport corners.
368 DrawDomainRing(batch, pixel, center, playerDomainRadius, mastery, animation);
383 DrawDomainRing(batch, pixel, center, playerDomainRadius, mastery, animation, drawBackdrop: false);
384 }
385
386 private void DrawDomainBackdropsAfterVanillaBackground(On_Main.orig_DrawBackground orig, Main self)
387 {
388 orig(self);
389 if (Main.gameMenu || !Main.LocalPlayer.active)
390 return;
391
392 bool anyFullScreen = false;
393 bool anyClipped = false;
394 for (int index = 0; index < Main.maxPlayers; index++)
395 {
396 if (domainAnimation[index] <= 0.001f || !Main.player[index].active)
397 continue;
398 anyFullScreen |= cachedFullScreen[index];
399 anyClipped |= !cachedFullScreen[index];
400 }
401 if (!anyFullScreen && !anyClipped)
402 return;
403
404 SpriteBatch batch = Main.spriteBatch;
405 if (anyFullScreen)
406 {
407 batch.Begin(
408 SpriteSortMode.Deferred,
409 BlendState.AlphaBlend,
410 SamplerState.LinearClamp,
411 DepthStencilState.None,
412 RasterizerState.CullNone,
413 null,
414 Matrix.Identity);
415 for (int index = 0; index < Main.maxPlayers; index++)
416 {
417 if (!cachedFullScreen[index] || domainAnimation[index] <= 0.001f || !Main.player[index].active)
418 continue;
419
420 DrawFullScreenBackdrop(
421 batch,
422 GetMasteryProgress(cachedMastery[index]),
423 MathHelper.SmoothStep(0f, 1f, domainAnimation[index]));
424 }
425 batch.End();
426 }
427
428 if (anyClipped)
429 {
430 batch.Begin(
431 SpriteSortMode.Deferred,
432 BlendState.AlphaBlend,
433 SamplerState.LinearClamp,
434 DepthStencilState.None,
435 RasterizerState.CullNone,
436 null,
437 Main.GameViewMatrix.TransformationMatrix);
438 for (int index = 0; index < Main.maxPlayers; index++)
439 {
440 Player player = Main.player[index];
441 float animation = domainAnimation[index];
442 if (cachedFullScreen[index] || animation <= 0.001f || !player.active)
443 continue;
444
445 float targetRadius = Math.Max(160f, cachedRadius[index]);
446 Vector2 center = player.Center - Main.screenPosition;
447 if (center.X < -targetRadius || center.X > Main.screenWidth + targetRadius
448 || center.Y < -targetRadius || center.Y > Main.screenHeight + targetRadius)
449 {
450 continue;
451 }
452
453 GetAnimatedBackdropGeometry(targetRadius, cachedMastery[index], animation,
454 out float coreRadius, out float noiseTime, out float noiseAmplitude,
455 out float masteryProgress, out float reveal);
456 DrawClippedDeathDomainBackdrop(
457 batch,
458 center,
459 coreRadius,
460 noiseTime,
461 16.2f,
462 noiseAmplitude,
463 masteryProgress,
464 reveal);
465 }
466 batch.End();
467 }
468 }
469
470 private static void GetAnimatedBackdropGeometry(
471 float targetRadius,
472 int mastery,
473 float animation,
474 out float coreRadius,
475 out float noiseTime,
476 out float noiseAmplitude,
477 out float masteryProgress,
478 out float reveal)
479 {
480 reveal = MathHelper.SmoothStep(0f, 1f, animation);
481 float rangeProgress = MathHelper.Clamp(
482 (targetRadius - 160f) / (DeathNecklace.RadiusGrowthPerLevel * (DeathNecklace.MaxCoreLevel - 1)),
483 0f,
484 1f);
485 float manifestedReveal = reveal * MathHelper.Lerp(0.38f, 1f, rangeProgress);
486 float expansion = 1f - (float)Math.Pow(1f - reveal, 3f);
487 float overshoot = 1f + (float)Math.Sin(reveal * MathHelper.Pi) * 0.035f;
488 float radius = MathHelper.Lerp(8f, targetRadius, expansion) * overshoot;
489 masteryProgress = GetMasteryProgress(mastery);
490 float coreGap = MathHelper.Lerp(22f, 14f, masteryProgress);
491 coreRadius = radius > coreGap + 6f ? radius - coreGap : radius * 0.58f;
492 noiseTime = Main.GlobalTimeWrappedHourly * 0.8f;
493 noiseAmplitude = Math.Min(
494 coreRadius * 0.18f,
495 MathHelper.Lerp(5f, 10f, masteryProgress) * MathHelper.Lerp(0.35f, 1f, manifestedReveal));
496 reveal = manifestedReveal;
369 497 }
370 498
371 499 private static void DrawFullScreenBackdrop(SpriteBatch batch, float mastery, float reveal)
372 500 {
373 501 Vector2 worldCenter = Main.screenPosition + new Vector2(Main.screenWidth, Main.screenHeight) * 0.5f;
502 float referenceDiameter = (160f + DeathNecklace.RadiusGrowthPerLevel * (DeathNecklace.MaxCoreLevel - 1)) * 2f;
503 float horizontalScale = Main.screenWidth / referenceDiameter;
504 float verticalScale = Main.screenHeight / referenceDiameter;
374 505 for (int layer = 0; layer < 3; layer++)
375 506 {
376 float parallax = layer switch { 0 => 0.07f, 1 => 0.18f, _ => 0.34f };
377 float horizontalViewFraction = layer switch { 0 => 0.68f, 1 => 0.9f, _ => 0.84f };
378 float verticalViewFraction = layer switch { 0 => 0.78f, 1 => 0.9f, _ => 0.86f };
379 507 float opacity = layer switch
380 508 {
381 509 0 => MathHelper.Lerp(0.88f, 1f, mastery) * reveal,
@@ -383,12 +511,8 @@ internal class DeathDomainVisualSystem : ModSystem
383 511 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
384 512 };
385 513 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
386 float viewWidth = texture.Width * horizontalViewFraction;
387 float viewHeight = texture.Height * verticalViewFraction;
388 float sourceOffsetX = PositiveModulo(worldCenter.X * parallax, texture.Width);
389 float worldHeight = Math.Max(1f, Main.maxTilesY * 16f);
390 float worldDepth = MathHelper.Clamp(worldCenter.Y / worldHeight, 0f, 1f);
391 float sourceOffsetY = worldDepth * Math.Max(0f, texture.Height - viewHeight);
514 GetBackdropSource(layer, texture, worldCenter, horizontalScale, verticalScale,
515 out float sourceOffsetX, out float sourceOffsetY, out float viewWidth, out float viewHeight);
392 516
393 517 DrawBackdropBand(
394 518 batch,
@@ -404,6 +528,32 @@ internal class DeathDomainVisualSystem : ModSystem
404 528 }
405 529 }
406 530
531 private static void GetBackdropSource(
532 int layer,
533 Texture2D texture,
534 Vector2 worldCenter,
535 float horizontalScale,
536 float verticalScale,
537 out float sourceOffsetX,
538 out float sourceOffsetY,
539 out float viewWidth,
540 out float viewHeight)
541 {
542 float referenceViewWidth = 1024f * (layer switch { 0 => 0.68f, 1 => 0.9f, _ => 0.84f });
543 float referenceViewHeight = 512f * (layer switch { 0 => 0.78f, 1 => 0.9f, _ => 0.86f });
544 viewWidth = Math.Min(texture.Width, referenceViewWidth * horizontalScale);
545 viewHeight = Math.Min(texture.Height, referenceViewHeight * verticalScale);
546
547 float worldWidth = Math.Max(1f, Main.maxTilesX * 16f);
548 float worldHeight = Math.Max(1f, Main.maxTilesY * 16f);
549 float worldX = MathHelper.Clamp(worldCenter.X / worldWidth, 0f, 1f);
550 float worldY = MathHelper.Clamp(worldCenter.Y / worldHeight, 0f, 1f);
551 float travelFactor = layer switch { 0 => 0.34f, 1 => 0.66f, _ => 1f };
552 float horizontalTravel = Math.Max(0f, texture.Width - viewWidth) * travelFactor;
553 sourceOffsetX = (texture.Width - viewWidth - horizontalTravel) * 0.5f + horizontalTravel * worldX;
554 sourceOffsetY = worldY * Math.Max(0f, texture.Height - viewHeight);
555 }
556
407 557 private static void DrawFilledNoiseDisc(
408 558 SpriteBatch batch,
409 559 Texture2D pixel,
Modified Common/MyPlayer.cs +1 -12
@@ -160,18 +160,7 @@ public class MyPlayer : ModPlayer
160 160 }
161 161
162 162 deathDomainSlashSoundCooldown = 4;
163 SoundEngine.PlaySound(SoundID.DD2_SonicBoomBladeSlash with
164 {
165 Volume = 0.58f + mastery * 0.12f + (requiem ? 0.08f : 0f),
166 Pitch = 0.20f + mastery * 0.08f,
167 PitchVariance = 0.045f
168 }, worldPosition);
169 SoundEngine.PlaySound(SoundID.Item1 with
170 {
171 Volume = 0.30f + mastery * 0.08f,
172 Pitch = 0.52f,
173 PitchVariance = 0.035f
174 }, worldPosition);
163 SoundEngine.PlaySound(SoundID.Item71, worldPosition);
175 164 }
176 165
177 166 internal void RecordDeathWingTrail(Vector2 upperTip, Vector2 lowerTip, float mastery)
Added Common/SickleTrailPrimitiveSystem.cs +131 -0
@@ -0,0 +1,131 @@
1 using DeathMod.Items;
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using System;
5 using System.Collections.Generic;
6 using Terraria;
7 using Terraria.ModLoader;
8
9 namespace DeathMod.Common;
10
11 [Autoload(Side = ModSide.Client)]
12 internal sealed class SickleTrailPrimitiveSystem : ModSystem
13 {
14 private const int CurveSubdivisions = 4;
15 private static BasicEffect? trailEffect;
16
17 internal static void DrawTrail(IReadOnlyList<Vector2> controlPoints, NormalSickle sickle, bool alternateAttack)
18 {
19 if (Main.dedServ || controlPoints.Count < 2)
20 return;
21
22 List<Vector2> curve = BuildSmoothCurve(controlPoints);
23 if (curve.Count < 2)
24 return;
25
26 BasicEffect effect = trailEffect ??= new BasicEffect(Main.instance.GraphicsDevice)
27 {
28 VertexColorEnabled = true,
29 TextureEnabled = false
30 };
31 VertexPositionColor[] outer = BuildStrip(curve, sickle, alternateAttack, core: false);
32 VertexPositionColor[] core = BuildStrip(curve, sickle, alternateAttack, core: true);
33 if (outer.Length < 4 || core.Length < 4)
34 return;
35
36 GraphicsDevice graphics = Main.instance.GraphicsDevice;
37 Main.spriteBatch.End();
38 graphics.BlendState = BlendState.AlphaBlend;
39 graphics.DepthStencilState = DepthStencilState.None;
40 graphics.RasterizerState = RasterizerState.CullNone;
41 effect.World = Main.GameViewMatrix.TransformationMatrix;
42 effect.View = Matrix.Identity;
43 effect.Projection = Matrix.CreateOrthographicOffCenter(0f, Main.screenWidth, Main.screenHeight, 0f, 0f, 1f);
44
45 foreach (EffectPass pass in effect.CurrentTechnique.Passes)
46 {
47 pass.Apply();
48 graphics.DrawUserPrimitives(PrimitiveType.TriangleStrip, outer, 0, outer.Length - 2);
49 graphics.DrawUserPrimitives(PrimitiveType.TriangleStrip, core, 0, core.Length - 2);
50 }
51
52 Main.spriteBatch.Begin(
53 SpriteSortMode.Deferred,
54 BlendState.AlphaBlend,
55 SamplerState.PointClamp,
56 DepthStencilState.None,
57 RasterizerState.CullNone,
58 null,
59 Main.GameViewMatrix.TransformationMatrix);
60 }
61
62 public override void Unload()
63 {
64 BasicEffect? oldEffect = trailEffect;
65 trailEffect = null;
66 if (oldEffect is not null && !Main.dedServ)
67 Main.QueueMainThreadAction(oldEffect.Dispose);
68 }
69
70 private static List<Vector2> BuildSmoothCurve(IReadOnlyList<Vector2> controlPoints)
71 {
72 List<Vector2> result = new((controlPoints.Count - 1) * CurveSubdivisions + 1)
73 {
74 controlPoints[0]
75 };
76 for (int segment = 0; segment < controlPoints.Count - 1; segment++)
77 {
78 Vector2 p0 = controlPoints[Math.Max(0, segment - 1)];
79 Vector2 p1 = controlPoints[segment];
80 Vector2 p2 = controlPoints[segment + 1];
81 Vector2 p3 = controlPoints[Math.Min(controlPoints.Count - 1, segment + 2)];
82 for (int subdivision = 1; subdivision <= CurveSubdivisions; subdivision++)
83 {
84 float amount = subdivision / (float)CurveSubdivisions;
85 Vector2 point = Vector2.CatmullRom(p0, p1, p2, p3, amount);
86 if (Vector2.DistanceSquared(result[^1], point) > 0.04f)
87 result.Add(point);
88 }
89 }
90 return result;
91 }
92
93 private static VertexPositionColor[] BuildStrip(
94 IReadOnlyList<Vector2> points,
95 NormalSickle sickle,
96 bool alternateAttack,
97 bool core)
98 {
99 VertexPositionColor[] vertices = new VertexPositionColor[points.Count * 2];
100 float attackScale = alternateAttack ? 1.25f : 1f;
101 for (int index = 0; index < points.Count; index++)
102 {
103 float progress = index / (points.Count - 1f);
104 float strength = progress * progress * (3f - 2f * progress);
105 Vector2 previous = points[Math.Max(0, index - 1)];
106 Vector2 next = points[Math.Min(points.Count - 1, index + 1)];
107 Vector2 normal = (next - previous).SafeNormalize(Vector2.UnitX).RotatedBy(MathHelper.PiOver2);
108
109 float width = sickle.TrailWidth * attackScale * MathHelper.Lerp(0.08f, 1f, strength);
110 if (sickle.SwingStyle == SickleSwingStyle.Rising)
111 width *= 0.8f + strength * 0.65f;
112 else if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
113 width *= 1f + (float)Math.Sin(progress * MathHelper.Pi * 5f + Main.GlobalTimeWrappedHourly * 8f) * 0.08f;
114 if (core)
115 width *= 0.42f;
116 else
117 width += 4f;
118
119 float opacity = MathHelper.Lerp(0f, core ? 0.68f : 0.38f, (float)Math.Pow(strength, 0.72f));
120 Color color = core
121 ? Color.Lerp(sickle.SickleSecondaryColor, Color.White, strength * 0.18f)
122 : Color.Lerp(sickle.SickleColor, sickle.SickleSecondaryColor, strength * 0.22f);
123 color *= opacity;
124 Vector2 screenPoint = points[index] - Main.screenPosition;
125 Vector2 halfWidth = normal * Math.Max(0.5f, width * 0.5f);
126 vertices[index * 2] = new VertexPositionColor(new Vector3(screenPoint - halfWidth, 0f), color);
127 vertices[index * 2 + 1] = new VertexPositionColor(new Vector3(screenPoint + halfWidth, 0f), color);
128 }
129 return vertices;
130 }
131 }
Modified Items/DeathWings.cs +5 -3
@@ -154,7 +154,7 @@ public class DeathWings : ModItem, IDeathAltarUpgradeable
154 154 DeathAltarUpgradeType.WingVerticalPower => LevelPrice(VerticalPowerLevel, 45),
155 155 DeathAltarUpgradeType.WingInfiniteFlight => InfiniteFlightUnlocked || !AllFlightStatsMaxed
156 156 ? DeathAltarPrice.Unavailable
157 : DeathAltarPrice.Essence(25),
157 : DeathAltarPrice.Essence(HalvedCost(25)),
158 158 _ => DeathAltarPrice.Unavailable
159 159 };
160 160 }
@@ -284,10 +284,12 @@ public class DeathWings : ModItem, IDeathAltarUpgradeable
284 284 if (level >= MaxWingLevel)
285 285 return DeathAltarPrice.Unavailable;
286 286 return level < 6
287 ? DeathAltarPrice.Souls(level * soulMultiplier)
288 : DeathAltarPrice.Essence(level);
287 ? DeathAltarPrice.Souls(HalvedCost(level * soulMultiplier))
288 : DeathAltarPrice.Essence(HalvedCost(level));
289 289 }
290 290
291 private static int HalvedCost(int cost) => Math.Max(1, (cost + 1) / 2);
292
291 293 private static float LevelProgress(int level)
292 294 {
293 295 return MathHelper.Clamp((level - 1) / 9f, 0f, 1f);
Modified Items/NormalSickle.cs +9 -2
@@ -121,7 +121,6 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
121 121 public virtual int MaximumChargeFrames => 55 + SickleTier * 8;
122 122 public virtual int SickleReach => 74 + SickleTier * 10;
123 123 public virtual float SickleCollisionWidth => 18f + SickleTier * 3f;
124 public virtual int ChargedProjectileBonus => SickleTier >= 2 ? 2 : 1;
125 124 public virtual Color SickleColor => new(80, 205, 225);
126 125 public virtual Color SickleSecondaryColor => new(210, 250, 255);
127 126 public virtual int SickleDustType => DustID.AncientLight;
@@ -470,7 +469,15 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
470 469 if (!SupportsProjectileCount || AttackProjectileType <= ProjectileID.None)
471 470 return;
472 471
473 int count = Math.Clamp(ProjectileCount + (heavySlash ? ChargedProjectileBonus : 0), 1, MaxProjectileCount + ChargedProjectileBonus);
472 int count = ProjectileCount;
473 if (heavySlash)
474 {
475 float projectileMultiplier = SupportsCharge
476 ? MathHelper.Lerp(1f, 2f, MathHelper.Clamp(charge, 0f, 1f))
477 : 1.5f;
478 count = Math.Max(1, (int)Math.Ceiling(ProjectileCount * projectileMultiplier));
479 }
480 count = Math.Clamp(count, 1, MaxProjectileCount * 2);
474 481 if (heavySlash && AlternateAttackStyle == SickleAlternateAttackStyle.BoneBarrage)
475 482 count = Math.Max(3, count);
476 483 else if (heavySlash && AlternateAttackStyle == SickleAlternateAttackStyle.VoidRitual)
Modified Items/VoidSickle.cs +0 -1
@@ -16,7 +16,6 @@ public class VoidSickle : NormalSickle
16 16 public override int AlternateNextSickleType => ItemID.None;
17 17 public override SickleSwingStyle SwingStyle => SickleSwingStyle.VoidOrbit;
18 18 public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.VoidRitual;
19 public override int ChargedProjectileBonus => 1;
20 19 public override Color SickleColor => new(190, 55, 255);
21 20 public override Color SickleSecondaryColor => new(255, 85, 235);
22 21 public override int SickleDustType => DustID.Shadowflame;
Modified Projectiles/SickleSwingProjectile.cs +10 -56
@@ -3,6 +3,7 @@ using DeathMod.Items;
3 3 using Microsoft.Xna.Framework;
4 4 using Microsoft.Xna.Framework.Graphics;
5 5 using System;
6 using System.Collections.Generic;
6 7 using System.IO;
7 8 using Terraria;
8 9 using Terraria.Audio;
@@ -539,67 +540,20 @@ public class SickleSwingProjectile : ModProjectile
539 540
540 541 private void DrawSlashTrail(NormalSickle sickle)
541 542 {
542 Texture2D pixel = TextureAssets.MagicPixel.Value;
543 543 int segmentCount = Math.Min(HistoryLength - 1, 7 + sickle.SickleTier * 2);
544 float empoweredScale = IsAlternateAttack ? 1.25f : 1f;
545
546 for (int index = segmentCount - 1; index >= 0; index--)
544 List<Vector2> controlPoints = new(segmentCount + 1);
545 for (int index = segmentCount; index >= 0; index--)
547 546 {
548 if (gripHistory[index] == Vector2.Zero || gripHistory[index + 1] == Vector2.Zero)
549 continue;
550 if (sickle.SwingStyle is SickleSwingStyle.Overhead or SickleSwingStyle.CrossCut && index % 3 == 2)
551 continue;
552
553 Vector2 currentTip = GetBladeTip(sickle, gripHistory[index], angleHistory[index]);
554 Vector2 oldTip = GetBladeTip(sickle, gripHistory[index + 1], angleHistory[index + 1]);
555 Vector2 difference = oldTip - currentTip;
556 float length = difference.Length();
557 float maximumSegmentLength = 28f + sickle.SickleTier * 4f;
558 if (length < 0.5f || length > maximumSegmentLength)
547 if (gripHistory[index] == Vector2.Zero)
559 548 continue;
560
561 float strength = 1f - index / (float)segmentCount;
562 float width = sickle.TrailWidth * strength * empoweredScale;
563 if (sickle.SwingStyle == SickleSwingStyle.Rising)
564 width *= 0.8f + strength * 0.65f;
565 if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
566 width *= 1f + (float)Math.Sin(index * 1.7f + Main.GlobalTimeWrappedHourly * 8f) * 0.18f;
567
568 DrawTrailSegment(pixel, currentTip, difference, sickle.SickleColor with { A = 0 }, width + 4f, strength * 0.34f);
569 DrawTrailSegment(pixel, currentTip, difference, sickle.SickleSecondaryColor with { A = 0 }, width * 0.42f, strength * 0.62f);
570
571 if (sickle.SwingStyle == SickleSwingStyle.Circular)
572 {
573 Vector2 innerCurrent = Vector2.Lerp(gripHistory[index], currentTip, 0.82f);
574 Vector2 innerOld = Vector2.Lerp(gripHistory[index + 1], oldTip, 0.82f);
575 DrawTrailSegment(pixel, innerCurrent, innerOld - innerCurrent, sickle.SickleSecondaryColor with { A = 0 }, width * 0.32f, strength * 0.5f);
576 }
577 else if (sickle.SwingStyle == SickleSwingStyle.SoulOrbit)
578 {
579 float wave = (float)Math.Sin(index * 1.4f + Main.GlobalTimeWrappedHourly * 7f) * 7f;
580 Vector2 normal = difference.SafeNormalize(Vector2.UnitX).RotatedBy(MathHelper.PiOver2) * wave;
581 DrawTrailSegment(pixel, currentTip + normal, difference, sickle.SickleSecondaryColor with { A = 0 }, width * 0.28f, strength * 0.52f);
582 }
583 else if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
584 {
585 Vector2 midpoint = currentTip + difference * 0.5f;
586 float moteSize = 2.5f + strength * 4f;
587 Main.EntitySpriteDraw(pixel, midpoint - Main.screenPosition, null, sickle.SickleSecondaryColor with { A = 0 },
588 MathHelper.PiOver4, pixel.Size() * 0.5f,
589 new Vector2(moteSize / pixel.Width, moteSize / pixel.Height), SpriteEffects.None);
590 }
549 Vector2 tip = GetBladeTip(sickle, gripHistory[index], angleHistory[index]);
550 if (controlPoints.Count > 0 && Vector2.DistanceSquared(controlPoints[^1], tip) > 120f * 120f)
551 controlPoints.Clear();
552 if (controlPoints.Count == 0 || Vector2.DistanceSquared(controlPoints[^1], tip) > 0.25f)
553 controlPoints.Add(tip);
591 554 }
592 }
593
594 private static void DrawTrailSegment(Texture2D pixel, Vector2 start, Vector2 difference, Color color, float width, float opacity)
595 {
596 float length = difference.Length();
597 if (length <= 0.5f)
598 return;
599 555
600 Main.EntitySpriteDraw(pixel, start - Main.screenPosition, null, color * opacity,
601 difference.ToRotation(), new Vector2(0f, pixel.Height * 0.5f),
602 new Vector2(length / pixel.Width, Math.Max(1f, width) / pixel.Height), SpriteEffects.None);
556 SickleTrailPrimitiveSystem.DrawTrail(controlPoints, sickle, IsAlternateAttack);
603 557 }
604 558
605 559 private static void DrawWeapon(Texture2D texture, NormalSickle sickle, Vector2 position, float angle, bool flipped, Color color, float opacity, float scale)