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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

将领域黑核重制为视差背景

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

代码差异

4 个文件 +364 -133
Added Common/DeathDomainBackdropTextureSystem.cs +218 -0
@@ -0,0 +1,218 @@
1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using System;
4 using Terraria;
5 using Terraria.ModLoader;
6
7 namespace DeathMod.Common;
8
9 /// <summary>
10 /// Procedurally builds the three seamless plates used by the Death Domain's
11 /// clipped parallax window. Textures are created lazily from the draw thread so
12 /// no FNA graphics resource is ever allocated by a content-loader worker.
13 /// </summary>
14 [Autoload(Side = ModSide.Client)]
15 internal sealed class DeathDomainBackdropTextureSystem : ModSystem
16 {
17 internal const int LayerWidth = 1024;
18 internal const int LayerHeight = 512;
19
20 private static readonly Texture2D?[] layers = new Texture2D?[3];
21
22 internal static Texture2D GetLayer(int index)
23 {
24 index = Math.Clamp(index, 0, layers.Length - 1);
25 return layers[index] ??= CreateLayer(index);
26 }
27
28 public override void Unload()
29 {
30 Texture2D?[] oldLayers = (Texture2D?[])layers.Clone();
31 Array.Clear(layers);
32 if (Main.dedServ)
33 return;
34
35 Main.QueueMainThreadAction(() =>
36 {
37 foreach (Texture2D? texture in oldLayers)
38 texture?.Dispose();
39 });
40 }
41
42 private static Texture2D CreateLayer(int layer)
43 {
44 Texture2D texture = new(Main.instance.GraphicsDevice, LayerWidth, LayerHeight, false, SurfaceFormat.Color);
45 Color[] pixels = new Color[LayerWidth * LayerHeight];
46 for (int y = 0; y < LayerHeight; y++)
47 {
48 float v = (y + 0.5f) / LayerHeight;
49 for (int x = 0; x < LayerWidth; x++)
50 {
51 float u = (x + 0.5f) / LayerWidth;
52 pixels[y * LayerWidth + x] = layer switch
53 {
54 0 => BuildFarPixel(x, y, u, v),
55 1 => BuildMiddlePixel(u, v),
56 _ => BuildNearPixel(x, u, v)
57 };
58 }
59 }
60
61 texture.SetData(pixels);
62 return texture;
63 }
64
65 private static Color BuildFarPixel(int x, int y, float u, float v)
66 {
67 Vector3 top = new(0.018f, 0.006f, 0.055f);
68 Vector3 bottom = new(0.105f, 0.004f, 0.072f);
69 Vector3 color = Vector3.Lerp(top, bottom, Smooth01(v));
70
71 float crimsonCloud = Wave(u, v, 2f, 1f, 0.13f)
72 + Wave(u, v, 5f, -2f, 1.7f) * 0.45f
73 + Wave(u, v, 9f, 3f, 4.2f) * 0.2f;
74 crimsonCloud = Smooth01((crimsonCloud + 1.1f) / 2.45f);
75 float violetCloud = Smooth01((Wave(u, v, 3f, -2f, 3.4f) + 0.78f) / 1.78f);
76 color += new Vector3(0.16f, 0.006f, 0.055f) * crimsonCloud * (0.32f + v * 0.34f);
77 color += new Vector3(0.035f, 0.018f, 0.13f) * violetCloud * 0.42f;
78
79 float hash = Hash01(x, y, 9137);
80 if (hash > 0.9962f)
81 {
82 float star = MathHelper.Clamp((hash - 0.9962f) / 0.0038f, 0f, 1f);
83 Vector3 starColor = (x + y) % 5 == 0
84 ? new Vector3(1f, 0.22f, 0.34f)
85 : new Vector3(0.62f, 0.38f, 0.92f);
86 color = Vector3.Lerp(color, starColor, 0.42f + star * 0.58f);
87 }
88
89 return Opaque(color);
90 }
91
92 private static Color BuildMiddlePixel(float u, float v)
93 {
94 Vector3 color = Vector3.Zero;
95 float alpha = 0f;
96
97 // A distant eclipsed blood moon with a luminous, broken corona.
98 Vector2 moonOffset = new((u - 0.28f) * 2f, (v - 0.28f) * 4f);
99 float moonDistance = moonOffset.Length();
100 float corona = 1f - SmoothRange(0.19f, 0.245f, moonDistance);
101 float hollow = SmoothRange(0.115f, 0.17f, moonDistance);
102 float moonRing = MathHelper.Clamp(corona * hollow, 0f, 1f);
103 if (moonRing > 0f)
104 {
105 float fracture = 0.72f + 0.28f * (float)Math.Sin(Math.Atan2(moonOffset.Y, moonOffset.X) * 11f);
106 alpha = moonRing * fracture * 0.88f;
107 color = new Vector3(0.72f, 0.025f, 0.15f);
108 }
109
110 float cloud = Smooth01((Wave(u, v, 4f, 1f, 0.7f) + Wave(u, v, 7f, -2f, 2.9f) * 0.55f + 0.55f) / 2.1f);
111 float cloudAlpha = cloud * (1f - Math.Abs(v - 0.48f) * 1.55f) * 0.2f;
112 BlendPremultiplied(ref color, ref alpha, new Vector3(0.42f, 0.02f, 0.13f), Math.Max(0f, cloudAlpha));
113
114 // Two ranges of dead mountains create the mid-distance depth plane.
115 float rearHorizon = 0.59f
116 + (float)Math.Sin(u * MathHelper.TwoPi * 3f + 0.4f) * 0.06f
117 + (float)Math.Sin(u * MathHelper.TwoPi * 7f) * 0.025f;
118 if (v > rearHorizon)
119 {
120 float depth = Smooth01((v - rearHorizon) / 0.18f);
121 BlendPremultiplied(ref color, ref alpha, new Vector3(0.085f, 0.012f, 0.12f), 0.62f + depth * 0.14f);
122 }
123
124 float frontHorizon = 0.71f
125 + (float)Math.Sin(u * MathHelper.TwoPi * 5f + 2.1f) * 0.055f
126 + (float)Math.Sin(u * MathHelper.TwoPi * 11f) * 0.018f;
127 if (v > frontHorizon)
128 BlendPremultiplied(ref color, ref alpha, new Vector3(0.035f, 0.004f, 0.055f), 0.82f);
129
130 return Premultiplied(color, alpha);
131 }
132
133 private static Color BuildNearPixel(int x, float u, float v)
134 {
135 const int cellWidth = 64;
136 int cell = x / cellWidth;
137 float cellPosition = (x % cellWidth + 0.5f) / cellWidth;
138 float seed = Hash01(cell, 0, 4271);
139 float halfWidth = MathHelper.Lerp(0.18f, 0.34f, Hash01(cell, 1, 7727));
140 float spireTip = MathHelper.Lerp(0.43f, 0.64f, seed);
141 float towerShoulder = spireTip + Math.Abs(cellPosition - 0.5f) / halfWidth * 0.18f;
142 bool inTower = Math.Abs(cellPosition - 0.5f) <= halfWidth && v >= towerShoulder;
143 float ground = 0.86f + (float)Math.Sin(u * MathHelper.TwoPi * 8f) * 0.018f;
144 bool inGround = v >= ground;
145 if (!inTower && !inGround)
146 return Color.Transparent;
147
148 float edgeDistance = Math.Abs(v - towerShoulder);
149 Vector3 color = edgeDistance < 0.008f && inTower
150 ? new Vector3(0.36f, 0.012f, 0.075f)
151 : new Vector3(0.012f, 0.002f, 0.026f);
152 float alpha = inGround ? 0.96f : 0.88f;
153
154 // Narrow crimson windows keep the skyline readable without brightening it.
155 bool window = inTower
156 && Math.Abs(cellPosition - 0.5f) < 0.035f
157 && v > towerShoulder + 0.07f
158 && PositiveModulo(v * 34f + cell * 0.73f, 1f) < 0.18f;
159 if (window)
160 color = new Vector3(0.68f, 0.012f, 0.09f);
161
162 return Premultiplied(color, alpha);
163 }
164
165 private static void BlendPremultiplied(ref Vector3 color, ref float alpha, Vector3 sourceColor, float sourceAlpha)
166 {
167 sourceAlpha = MathHelper.Clamp(sourceAlpha, 0f, 1f);
168 float combinedAlpha = sourceAlpha + alpha * (1f - sourceAlpha);
169 if (combinedAlpha <= 0.0001f)
170 return;
171
172 color = (sourceColor * sourceAlpha + color * alpha * (1f - sourceAlpha)) / combinedAlpha;
173 alpha = combinedAlpha;
174 }
175
176 private static Color Opaque(Vector3 color) => new(
177 MathHelper.Clamp(color.X, 0f, 1f),
178 MathHelper.Clamp(color.Y, 0f, 1f),
179 MathHelper.Clamp(color.Z, 0f, 1f),
180 1f);
181
182 private static Color Premultiplied(Vector3 color, float alpha)
183 {
184 alpha = MathHelper.Clamp(alpha, 0f, 1f);
185 color *= alpha;
186 return new Color(
187 MathHelper.Clamp(color.X, 0f, 1f),
188 MathHelper.Clamp(color.Y, 0f, 1f),
189 MathHelper.Clamp(color.Z, 0f, 1f),
190 alpha);
191 }
192
193 private static float Wave(float u, float v, float horizontalCycles, float verticalCycles, float phase) =>
194 (float)Math.Sin(MathHelper.TwoPi * (u * horizontalCycles + v * verticalCycles) + phase);
195
196 private static float Hash01(int x, int y, int seed)
197 {
198 uint value = (uint)(x * 374761393 + y * 668265263 + seed * 1442695041);
199 value = (value ^ (value >> 13)) * 1274126177u;
200 value ^= value >> 16;
201 return (value & 0x00FFFFFFu) / 16777215f;
202 }
203
204 private static float SmoothRange(float start, float end, float value) =>
205 Smooth01((value - start) / Math.Max(0.0001f, end - start));
206
207 private static float Smooth01(float value)
208 {
209 value = MathHelper.Clamp(value, 0f, 1f);
210 return value * value * (3f - 2f * value);
211 }
212
213 private static float PositiveModulo(float value, float modulus)
214 {
215 float result = value % modulus;
216 return result < 0f ? result + modulus : result;
217 }
218 }
Modified Common/DeathDomainPrimitiveTextureSystem.cs +0 -48
@@ -134,54 +134,6 @@ internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
134 134 DrawBlade(start, rotation, length, width, color, completion);
135 135 }
136 136
137 internal static void DrawBladeRange(
138 Vector2 start,
139 float rotation,
140 float length,
141 float width,
142 Color color,
143 float completion,
144 float rangeStart,
145 float rangeEnd)
146 {
147 Texture2D? texture = BladeMask;
148 if (texture is null && !Main.dedServ)
149 {
150 texture = CreateBladeMask();
151 BladeMask = texture;
152 }
153
154 completion = MathHelper.Clamp(completion, 0f, 1f);
155 rangeStart = MathHelper.Clamp(rangeStart, 0f, 1f);
156 rangeEnd = MathHelper.Clamp(rangeEnd, rangeStart, 1f);
157 if (texture is null
158 || length <= 0.05f
159 || width <= 0.05f
160 || completion <= 0.001f
161 || rangeEnd - rangeStart <= 0.0001f)
162 {
163 return;
164 }
165
166 float visibleLength = Math.Max(0.5f, length * completion);
167 int sourceStart = Math.Clamp((int)Math.Floor(rangeStart * texture.Width), 0, texture.Width - 1);
168 int sourceEnd = Math.Clamp((int)Math.Ceiling(rangeEnd * texture.Width), sourceStart + 1, texture.Width);
169 Rectangle source = new(sourceStart, 0, sourceEnd - sourceStart, texture.Height);
170 float widthGrowth = MathHelper.Lerp(0.22f, 1f, SmoothStep(0f, 0.55f, completion));
171 Vector2 position = start + rotation.ToRotationVector2() * (visibleLength * sourceStart / texture.Width);
172
173 Main.EntitySpriteDraw(
174 texture,
175 position,
176 source,
177 color,
178 rotation,
179 new Vector2(0f, texture.Height * 0.5f),
180 new Vector2(visibleLength / texture.Width, width * widthGrowth / texture.Height),
181 SpriteEffects.None,
182 0f);
183 }
184
185 137 internal static void DrawRadialGradient(SpriteBatch batch, Vector2 center, float radius, Color color)
186 138 {
187 139 if (Main.dedServ || radius <= 0.5f)
Modified Common/DeathDomainVisualSystem.cs +144 -4
@@ -184,8 +184,25 @@ internal class DeathDomainVisualSystem : ModSystem
184 184 float coreNoiseTime = time * 0.8f;
185 185 const float coreNoiseSeed = 16.2f;
186 186 float coreNoiseAmplitude = Math.Min(coreRadius * 0.18f, MathHelper.Lerp(5f, 10f, mastery) * MathHelper.Lerp(0.35f, 1f, reveal));
187 float blackOpacity = MathHelper.Lerp(0.50f, 0.96f, mastery) * reveal;
188 DrawFilledNoiseDisc(batch, pixel, center, coreRadius, coreNoiseTime, coreNoiseSeed, coreNoiseAmplitude, Color.Black * blackOpacity);
187 DrawClippedDeathDomainBackdrop(
188 batch,
189 center,
190 coreRadius,
191 coreNoiseTime,
192 coreNoiseSeed,
193 coreNoiseAmplitude,
194 time,
195 mastery,
196 reveal);
197 DrawFilledNoiseDisc(
198 batch,
199 pixel,
200 center,
201 coreRadius,
202 coreNoiseTime,
203 coreNoiseSeed,
204 coreNoiseAmplitude,
205 new Color(3, 0, 10) * (MathHelper.Lerp(0.08f, 0.26f, mastery) * reveal));
189 206 DeathDomainPrimitiveTextureSystem.DrawRadialGradient(
190 207 batch,
191 208 center,
@@ -196,6 +213,129 @@ internal class DeathDomainVisualSystem : ModSystem
196 213 DrawInwardSuctionWaves(batch, pixel, center, radius, coreRadius, time, mastery, pulse);
197 214 }
198 215
216 private static void DrawClippedDeathDomainBackdrop(
217 SpriteBatch batch,
218 Vector2 center,
219 float baseRadius,
220 float noiseTime,
221 float noiseSeed,
222 float noiseAmplitude,
223 float time,
224 float mastery,
225 float reveal)
226 {
227 const int boundarySegments = 192;
228 const float preferredBandHeight = 2f;
229 Span<Vector2> boundary = stackalloc Vector2[boundarySegments];
230 float extent = 0f;
231 for (int index = 0; index < boundarySegments; index++)
232 {
233 float angle = MathHelper.TwoPi * index / boundarySegments;
234 float radius = Math.Max(1f, baseRadius + BoundaryNoise(angle, noiseTime, noiseSeed) * noiseAmplitude);
235 boundary[index] = angle.ToRotationVector2() * radius;
236 extent = Math.Max(extent, Math.Abs(boundary[index].Y));
237 }
238
239 int bands = Math.Max(1, (int)Math.Ceiling(extent * 2f / preferredBandHeight));
240 float bandHeight = extent * 2f / bands;
241 Vector2 worldCenter = center + Main.screenPosition;
242 // Draw one complete layer at a time. This retains proper alpha compositing
243 // while allowing SpriteBatch to group hundreds of clipped scan bands into
244 // only three texture batches instead of switching textures for every row.
245 for (int layer = 0; layer < 3; layer++)
246 {
247 float parallax = layer switch { 0 => 0.07f, 1 => 0.18f, _ => 0.34f };
248 Vector2 drift = layer switch
249 {
250 0 => new Vector2(5.5f, -1.5f),
251 1 => new Vector2(-11f, 2.5f),
252 _ => new Vector2(17f, -3.5f)
253 } * time;
254 float opacity = layer switch
255 {
256 0 => MathHelper.Lerp(0.88f, 1f, mastery) * reveal,
257 1 => MathHelper.Lerp(0.72f, 0.9f, mastery) * reveal,
258 _ => MathHelper.Lerp(0.78f, 0.96f, mastery) * reveal
259 };
260 Texture2D texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
261
262 for (int index = 0; index < bands; index++)
263 {
264 float scanY = -extent + (index + 0.5f) * bandHeight;
265 float left = float.MaxValue;
266 float right = float.MinValue;
267 for (int edge = 0; edge < boundarySegments; edge++)
268 {
269 Vector2 start = boundary[edge];
270 Vector2 end = boundary[(edge + 1) % boundarySegments];
271 if ((start.Y <= scanY && end.Y > scanY) || (end.Y <= scanY && start.Y > scanY))
272 {
273 float amount = (scanY - start.Y) / (end.Y - start.Y);
274 float intersectionX = MathHelper.Lerp(start.X, end.X, amount);
275 left = Math.Min(left, intersectionX);
276 right = Math.Max(right, intersectionX);
277 }
278 }
279
280 if (left >= right)
281 continue;
282
283 DrawBackdropBand(
284 batch,
285 texture,
286 new Vector2(center.X + left, center.Y + scanY - bandHeight * 0.5f),
287 right - left,
288 bandHeight,
289 worldCenter * parallax + new Vector2(left, scanY),
290 drift,
291 opacity);
292 }
293 }
294 }
295
296 private static void DrawBackdropBand(
297 SpriteBatch batch,
298 Texture2D texture,
299 Vector2 destination,
300 float destinationWidth,
301 float destinationHeight,
302 Vector2 sampleOrigin,
303 Vector2 drift,
304 float opacity)
305 {
306 if (destinationWidth <= 0.05f || destinationHeight <= 0.05f || opacity <= 0.001f)
307 return;
308
309 int sourceHeight = Math.Clamp((int)Math.Ceiling(destinationHeight), 1, texture.Height);
310 int sourceY = (int)PositiveModulo(sampleOrigin.Y + drift.Y, texture.Height);
311 sourceY = Math.Min(sourceY, texture.Height - sourceHeight);
312 float sourceX = PositiveModulo(sampleOrigin.X + drift.X, texture.Width);
313 float remaining = destinationWidth;
314 float destinationX = destination.X;
315 while (remaining > 0.01f)
316 {
317 int sourceStart = Math.Clamp((int)sourceX, 0, texture.Width - 1);
318 float available = texture.Width - sourceStart;
319 float drawWidth = Math.Min(remaining, available);
320 int sourceWidth = Math.Clamp((int)Math.Ceiling(drawWidth), 1, texture.Width - sourceStart);
321 Rectangle source = new(sourceStart, sourceY, sourceWidth, sourceHeight);
322 batch.Draw(
323 texture,
324 new Vector2(destinationX, destination.Y),
325 source,
326 Color.White * opacity,
327 0f,
328 Vector2.Zero,
329 new Vector2(drawWidth / sourceWidth, destinationHeight / sourceHeight),
330 SpriteEffects.None,
331 0f);
332
333 remaining -= drawWidth;
334 destinationX += drawWidth;
335 sourceX = 0f;
336 }
337 }
338
199 339 private static void DrawSovereignSeal(SpriteBatch batch, Texture2D pixel, Vector2 center, int mastery, float animation)
200 340 {
201 341 float time = Main.GlobalTimeWrappedHourly;
@@ -490,8 +630,8 @@ internal class DeathDomainVisualSystem : ModSystem
490 630
491 631 private static float GetMasteryProgress(int mastery)
492 632 {
493 // A fresh necklace has a mastery score of 5; a fully completed tree reaches 70.
494 return MathHelper.Clamp((mastery - 5f) / 65f, 0f, 1f);
633 // A fresh necklace has a mastery score of 5; a fully completed tree reaches 75.
634 return MathHelper.Clamp((mastery - 5f) / 70f, 0f, 1f);
495 635 }
496 636
497 637 private void ClearAnimationState()
Modified Projectiles/DeathDomainHarvestSlashProjectile.cs +2 -81
@@ -138,44 +138,12 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
138 138 float width = (21f + Mastery * 13f + (IsRequiem ? 8f : 0f)) * Projectile.scale * 0.78f;
139 139 Vector2 center = Projectile.Center - Main.screenPosition;
140 140
141 Vector2 direction = rotation.ToRotationVector2();
142 float bloodReveal = Smooth01((progress - 0.005f) / 0.12f);
143 float bloodFade = 1f - Smooth01((progress - 0.16f) / 0.80f);
144 float bloodStrength = bloodReveal * bloodFade;
145 float bloodLength = length * (3f + Mastery * 0.47f + (IsRequiem ? 0.3f : 0f));
146 Vector2 bloodStart = center - direction * bloodLength * 0.5f;
147
148 // Draw the execution body first. The crossing blood-line is split around
149 // the currently visible black core below, so no crimson pixels are ever
150 // submitted beneath that area; the darkness no longer depends on overdraw.
151 141 DrawTaperedBladeLayer(center, rotation, length, width * 1.52f * impactPulse, reveal,
152 142 new Color(92, 0, 22, 0) * (strength * 0.54f));
153 143 DrawTaperedBladeLayer(center, rotation, length, width * impactPulse, reveal,
154 144 new Color(255, 8, 48, 255) * strength);
155 float blackCoreLength = length * 0.985f;
156 DrawTaperedBladeLayer(center, rotation, blackCoreLength, width * 0.62f * impactPulse, reveal,
145 DrawTaperedBladeLayer(center, rotation, length * 0.985f, width * 0.62f * impactPulse, reveal,
157 146 Color.Black * Math.Min(1f, strength * 1.32f));
158
159 Vector2 blackCoreStart = center - direction * blackCoreLength * 0.5f;
160 float blackCoreVisibleLength = blackCoreLength * reveal;
161 DrawBladeOutsideSpan(
162 bloodStart,
163 rotation,
164 bloodLength,
165 (10f + Mastery * 3.5f) * Projectile.scale,
166 new Color(95, 0, 24, 0) * (bloodStrength * 0.72f),
167 bloodReveal,
168 blackCoreStart,
169 blackCoreVisibleLength);
170 DrawBladeOutsideSpan(
171 bloodStart,
172 rotation,
173 bloodLength,
174 (2.4f + Mastery * 0.85f) * Projectile.scale,
175 new Color(255, 0, 38, 0) * bloodStrength,
176 bloodReveal,
177 blackCoreStart,
178 blackCoreVisibleLength);
179 147 }
180 148
181 149 private static void DrawTaperedBladeLayer(Vector2 center, float rotation, float length, float maximumWidth, float reveal, Color color)
@@ -187,53 +155,6 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
187 155 DeathDomainPrimitiveTextureSystem.DrawBlade(start, rotation, length, maximumWidth, color, reveal);
188 156 }
189 157
190 private static void DrawBladeOutsideSpan(
191 Vector2 start,
192 float rotation,
193 float length,
194 float width,
195 Color color,
196 float reveal,
197 Vector2 excludedStart,
198 float excludedLength)
199 {
200 float visibleLength = length * MathHelper.Clamp(reveal, 0f, 1f);
201 if (visibleLength <= 0.05f || width <= 0.05f)
202 return;
203
204 Vector2 direction = rotation.ToRotationVector2();
205 float exclusionStart = Vector2.Dot(excludedStart - start, direction);
206 float exclusionEnd = exclusionStart + Math.Max(0f, excludedLength);
207 float firstLength = MathHelper.Clamp(exclusionStart, 0f, visibleLength);
208 if (firstLength > 0.05f)
209 {
210 DeathDomainPrimitiveTextureSystem.DrawBladeRange(
211 start,
212 rotation,
213 length,
214 width,
215 color,
216 reveal,
217 0f,
218 firstLength / visibleLength);
219 }
220
221 float secondStart = MathHelper.Clamp(exclusionEnd, 0f, visibleLength);
222 float secondLength = visibleLength - secondStart;
223 if (secondLength > 0.05f)
224 {
225 DeathDomainPrimitiveTextureSystem.DrawBladeRange(
226 start,
227 rotation,
228 length,
229 width,
230 color,
231 reveal,
232 secondStart / visibleLength,
233 1f);
234 }
235 }
236
237 158 private void SpawnExecutionBurst(int slash)
238 159 {
239 160 float rotation = GetSlashRotation(Projectile.rotation, slash);
@@ -301,7 +222,7 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
301 222 internal static float GetSlashLength(float mastery, float scale, int slash)
302 223 {
303 224 float variation = 0.94f + 0.06f * (float)Math.Sin((slash + 1) * 2.7f);
304 return (154f + mastery * 90f) * scale * variation;
225 return (188f + mastery * 108f) * scale * variation;
305 226 }
306 227
307 228 private static float Smooth01(float value)