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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

重制领域刀光并加入边缘牵引

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

代码差异

7 个文件 +267 -116
Added Common/DeathDomainPrimitiveTextureSystem.cs +91 -0
@@ -0,0 +1,91 @@
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 /// Builds a high-resolution, antialiased double-pointed mask for death-domain
11 /// blades. Drawing a single stretched mask avoids the seams and blocky corners
12 /// produced by assembling a slash out of dozens of MagicPixel rectangles.
13 /// </summary>
14 [Autoload(Side = ModSide.Client)]
15 internal sealed class DeathDomainPrimitiveTextureSystem : ModSystem
16 {
17 internal static Texture2D? BladeMask { get; private set; }
18
19 public override void PostSetupContent()
20 {
21 if (Main.dedServ)
22 return;
23
24 const int width = 1024;
25 const int height = 256;
26 Texture2D texture = new(Main.instance.GraphicsDevice, width, height, false, SurfaceFormat.Color);
27 Color[] pixels = new Color[width * height];
28
29 for (int y = 0; y < height; y++)
30 {
31 float vertical = Math.Abs((y + 0.5f) / height * 2f - 1f);
32 for (int x = 0; x < width; x++)
33 {
34 float horizontal = (x + 0.5f) / width;
35 float envelope = (float)Math.Pow(Math.Max(0f, Math.Sin(horizontal * MathHelper.Pi)), 0.62f);
36 float signedDistance = envelope <= 0.0001f ? 4f : vertical / envelope;
37 float alpha = 1f - SmoothStep(0.955f, 1.035f, signedDistance);
38
39 // Runtime textures are not processed by the content pipeline, so
40 // store premultiplied white explicitly for clean tinted edges.
41 byte value = (byte)Math.Clamp((int)Math.Round(alpha * 255f), 0, 255);
42 pixels[y * width + x] = new Color(value, value, value, value);
43 }
44 }
45
46 texture.SetData(pixels);
47 BladeMask = texture;
48 }
49
50 public override void Unload()
51 {
52 BladeMask?.Dispose();
53 BladeMask = null;
54 }
55
56 internal static void DrawBlade(
57 Vector2 start,
58 float rotation,
59 float length,
60 float width,
61 Color color,
62 float completion = 1f)
63 {
64 Texture2D? texture = BladeMask;
65 completion = MathHelper.Clamp(completion, 0f, 1f);
66 if (texture is null || length <= 0.05f || width <= 0.05f || completion <= 0.001f)
67 return;
68
69 float visibleLength = Math.Max(0.5f, length * completion);
70 float widthGrowth = MathHelper.Lerp(0.22f, 1f, SmoothStep(0f, 0.55f, completion));
71 Main.EntitySpriteDraw(
72 texture,
73 start,
74 null,
75 color,
76 rotation,
77 new Vector2(0f, texture.Height * 0.5f),
78 new Vector2(visibleLength / texture.Width, width * widthGrowth / texture.Height),
79 SpriteEffects.None,
80 0f);
81 }
82
83 private static float SmoothStep(float start, float end, float value)
84 {
85 if (end <= start)
86 return value >= end ? 1f : 0f;
87
88 value = MathHelper.Clamp((value - start) / (end - start), 0f, 1f);
89 return value * value * (3f - 2f * value);
90 }
91 }
Modified Common/DeathDomainVisualSystem.cs +31 -32
@@ -228,7 +228,7 @@ internal class DeathDomainVisualSystem : ModSystem
228 228 float noiseAmplitude,
229 229 Color color)
230 230 {
231 const int boundarySegments = 128;
231 const int boundarySegments = 192;
232 232 const float preferredBandHeight = 3f;
233 233 Span<Vector2> boundary = stackalloc Vector2[boundarySegments];
234 234 float extent = 0f;
@@ -282,7 +282,7 @@ internal class DeathDomainVisualSystem : ModSystem
282 282 float mastery,
283 283 bool gaps)
284 284 {
285 const int segments = 128;
285 const int segments = 256;
286 286 float previousAngle = 0f;
287 287 float previousRadius = baseRadius + BoundaryNoise(previousAngle, time, seed) * noiseAmplitude;
288 288 Vector2 previous = center + Vector2.UnitX * previousRadius;
@@ -330,48 +330,47 @@ internal class DeathDomainVisualSystem : ModSystem
330 330 float mastery,
331 331 float pulse)
332 332 {
333 int streamCount = 11 + (int)Math.Round(mastery * 7f);
334 float outerRadius = edgeRadius + MathHelper.Lerp(26f, 42f, mastery);
335 float innerRadius = Math.Max(4f, coreRadius - MathHelper.Lerp(4f, 12f, mastery));
333 // Long accretion streams outside the red boundary restore the visual
334 // language of a black-hole edge: distant, dark matter bends into a spiral
335 // and tightens as it reaches the event horizon.
336 int streamCount = 7 + (int)Math.Round(mastery * 5f);
337 float outerRadius = edgeRadius + MathHelper.Lerp(72f, 126f, mastery);
336 338 for (int stream = 0; stream < streamCount; stream++)
337 339 {
338 340 float seed = stream * 4.731f + 2.4f;
339 float progress = PositiveModulo(time * MathHelper.Lerp(0.42f, 0.68f, mastery) + stream / (float)streamCount, 1f);
340 float inwardProgress = MathHelper.SmoothStep(0f, 1f, progress);
341 float waveRadius = MathHelper.Lerp(outerRadius, innerRadius, inwardProgress);
341 float movingHead = PositiveModulo(time * MathHelper.Lerp(0.48f, 0.78f, mastery)
342 + stream / (float)streamCount, 1f);
342 343 float baseAngle = stream * MathHelper.TwoPi / streamCount
343 - time * (0.18f + mastery * 0.12f)
344 + inwardProgress * (0.28f + mastery * 0.2f)
345 + BoundaryNoise(stream, time * 0.24f, seed) * 0.07f;
346 float halfArc = MathHelper.Lerp(0.17f, 0.055f, inwardProgress);
347 float fade = (float)Math.Sin(progress * MathHelper.Pi) * pulse;
348 float width = MathHelper.Lerp(4.6f + mastery * 1.8f, 1.1f, inwardProgress);
349 Color outerColor = Color.Lerp(new Color(118, 4, 30), new Color(245, 22, 70), mastery);
350 Color innerColor = Color.Lerp(new Color(245, 28, 70), new Color(255, 115, 155), mastery);
351 Color color = Color.Lerp(outerColor, innerColor, inwardProgress) * (fade * 0.62f);
352
353 const int points = 9;
344 - time * (0.13f + mastery * 0.08f)
345 + BoundaryNoise(stream, time * 0.18f, seed) * 0.06f;
346
347 const int points = 46;
354 348 Vector2 previous = Vector2.Zero;
355 349 for (int point = 0; point < points; point++)
356 350 {
357 float alongArc = point / (points - 1f) * 2f - 1f;
358 float angle = baseAngle + alongArc * halfArc;
359 float taperedRadius = waveRadius
360 + BoundaryNoise(angle, -time * 1.35f, seed) * MathHelper.Lerp(3.8f, 1.2f, inwardProgress)
361 - Math.Abs(alongArc) * MathHelper.Lerp(1f, 4f, inwardProgress);
362 Vector2 current = center + angle.ToRotationVector2() * taperedRadius;
351 float inward = point / (points - 1f);
352 float curved = inward * inward * (3f - 2f * inward);
353 float angle = baseAngle + inward * MathHelper.Lerp(1.08f, 1.54f, mastery);
354 float radius = MathHelper.Lerp(outerRadius, edgeRadius + 2f, curved)
355 + BoundaryNoise(angle, -time * 0.82f, seed) * MathHelper.Lerp(5.2f, 1.2f, inward);
356 Vector2 current = center + angle.ToRotationVector2() * radius;
363 357 if (point > 0)
364 358 {
365 float taper = 1f - Math.Abs(alongArc) * 0.46f;
366 DrawSegment(batch, pixel, previous, current - previous, color * taper, width * taper);
359 float bodyFade = (float)Math.Sin(inward * MathHelper.Pi);
360 float distanceFromHead = Math.Abs(inward - movingHead);
361 distanceFromHead = Math.Min(distanceFromHead, 1f - distanceFromHead);
362 float movingGlow = 1f - MathHelper.SmoothStep(0.02f, 0.25f, distanceFromHead);
363 float opacity = bodyFade * pulse * (0.16f + movingGlow * 0.64f);
364 Color color = Color.Lerp(
365 new Color(18, 0, 8),
366 Color.Lerp(new Color(174, 3, 40), new Color(255, 34, 76), mastery),
367 inward * (0.45f + movingGlow * 0.55f));
368 float width = MathHelper.Lerp(0.55f, 3.4f + mastery * 1.7f, inward)
369 * (0.68f + movingGlow * 0.48f);
370 DrawSegment(batch, pixel, previous, current - previous, color * opacity, width);
367 371 }
368 372 previous = current;
369 373 }
370
371 // A short inward hook makes the direction of travel readable even in a still frame.
372 Vector2 hookStart = center + baseAngle.ToRotationVector2() * (waveRadius + width * 0.4f);
373 Vector2 hookEnd = center + (baseAngle + 0.035f).ToRotationVector2() * (waveRadius - MathHelper.Lerp(5f, 11f, inwardProgress));
374 DrawSegment(batch, pixel, hookStart, hookEnd - hookStart, color * 0.72f, Math.Max(0.8f, width * 0.62f));
375 374 }
376 375 }
377 376
Modified Common/MyGlobalNPC.cs +35 -0
@@ -27,6 +27,8 @@ public class MyGlobalNPC : GlobalNPC
27 27 private int lastSicklePlayer = -1;
28 28 private int fatedSecondTimer;
29 29 private bool deathMarked;
30 private int deathDomainHitStopFrames;
31 private Vector2 deathDomainStoredVelocity;
30 32
31 33 public int FatedStackCount { get; private set; }
32 34
@@ -39,9 +41,30 @@ public class MyGlobalNPC : GlobalNPC
39 41 lastSicklePlayer = -1;
40 42 fatedSecondTimer = 0;
41 43 deathMarked = false;
44 deathDomainHitStopFrames = 0;
45 deathDomainStoredVelocity = Vector2.Zero;
42 46 FatedStackCount = 0;
43 47 }
44 48
49 public override bool PreAI(NPC npc)
50 {
51 if (deathDomainHitStopFrames <= 0)
52 return true;
53
54 deathDomainHitStopFrames--;
55 npc.velocity = Vector2.Zero;
56 if (deathDomainHitStopFrames <= 0)
57 {
58 // Restore only part of the previous movement so the pause reads as an
59 // impact without launching fast enemies immediately out of the blade.
60 npc.velocity = deathDomainStoredVelocity * 0.35f;
61 deathDomainStoredVelocity = Vector2.Zero;
62 if (Main.netMode != NetmodeID.MultiplayerClient)
63 npc.netUpdate = true;
64 }
65 return false;
66 }
67
45 68 public override void ModifyHitByItem(NPC npc, Player player, Item item, ref NPC.HitModifiers modifiers)
46 69 {
47 70 if (item.ModItem is not (NormalSickle or LegacyDeath))
@@ -214,6 +237,18 @@ public class MyGlobalNPC : GlobalNPC
214 237 DeathMod.BroadcastDeathMark(npc, marked: true);
215 238 }
216 239
240 internal void ApplyDeathDomainHitStop(NPC npc, int frames)
241 {
242 frames = Math.Clamp(frames, 1, 4);
243 if (deathDomainHitStopFrames <= 0)
244 deathDomainStoredVelocity = npc.velocity;
245
246 deathDomainHitStopFrames = Math.Max(deathDomainHitStopFrames, frames);
247 npc.velocity = Vector2.Zero;
248 if (Main.netMode != NetmodeID.MultiplayerClient)
249 npc.netUpdate = true;
250 }
251
217 252 internal void ReceiveDeathMarkState(bool marked)
218 253 {
219 254 deathMarked = marked;
Modified Common/MyPlayer.cs +58 -0
@@ -370,6 +370,7 @@ public class MyPlayer : ModPlayer
370 370 return;
371 371 }
372 372
373 ApplyDeathDomainAttraction(necklace);
373 374 List<NPC> targets = FindDeathDomainTargets(necklace);
374 375 float visualMastery = MathHelper.Clamp((necklace.MasteryScore - 5f) / 65f, 0f, 1f);
375 376 UpdateDeathDomainTelegraphs(targets, necklace, visualMastery);
@@ -558,6 +559,63 @@ public class MyPlayer : ModPlayer
558 559 return targets;
559 560 }
560 561
562 private void ApplyDeathDomainAttraction(DeathNecklace necklace)
563 {
564 // A sovereign full-screen domain has no meaningful exterior on the active
565 // battlefield. Its targets are already inside, so no pull is necessary.
566 if (necklace.IsFullScreenDomain)
567 return;
568
569 float innerRadius = necklace.DomainRadius;
570 float outerRadius = necklace.DomainAttractionRadius;
571 float innerRadiusSquared = innerRadius * innerRadius;
572 float outerRadiusSquared = outerRadius * outerRadius;
573 float attractionWidth = Math.Max(1f, outerRadius - innerRadius);
574 HashSet<int> affectedTargets = [];
575
576 foreach (NPC npc in Main.ActiveNPCs)
577 {
578 if (npc.friendly || npc.immortal || npc.dontTakeDamage || npc.lifeMax <= 5 || npc.life <= 0)
579 continue;
580
581 NPC target = npc.realLife >= 0 && npc.realLife < Main.maxNPCs && Main.npc[npc.realLife].active
582 ? Main.npc[npc.realLife]
583 : npc;
584 if (target.friendly || target.immortal || target.dontTakeDamage || target.lifeMax <= 5 || target.life <= 0
585 || !affectedTargets.Add(target.whoAmI))
586 {
587 continue;
588 }
589
590 Vector2 toDomain = Player.Center - target.Center;
591 float distanceSquared = toDomain.LengthSquared();
592 // Crossing the red boundary terminates the force immediately. This is
593 // deliberately not a vortex that continues dragging enemies at centre.
594 if (distanceSquared <= innerRadiusSquared || distanceSquared > outerRadiusSquared || distanceSquared < 0.001f)
595 continue;
596
597 float distance = (float)Math.Sqrt(distanceSquared);
598 float normalizedDistance = MathHelper.Clamp((distance - innerRadius) / attractionWidth, 0f, 1f);
599 float edgeInfluence = 1f - normalizedDistance;
600 edgeInfluence = edgeInfluence * edgeInfluence * (3f - 2f * edgeInfluence);
601 bool bossOrBossPart = target.boss
602 || NPCID.Sets.ShouldBeCountedAsBoss[target.type]
603 || target.realLife >= 0 && target.realLife < Main.maxNPCs && Main.npc[target.realLife].boss;
604 float bossScale = bossOrBossPart ? 0.18f : 1f;
605 float force = necklace.DomainAttractionStrength * MathHelper.Lerp(0.22f, 1f, edgeInfluence) * bossScale;
606 float maximumInwardSpeed = necklace.DomainAttractionMaximumSpeed * (bossOrBossPart ? 0.35f : 1f);
607 Vector2 inward = toDomain / distance;
608 float currentInwardSpeed = Vector2.Dot(target.velocity, inward);
609 float allowedForce = Math.Max(0f, maximumInwardSpeed - currentInwardSpeed);
610 target.velocity += inward * Math.Min(force, allowedForce);
611
612 // Periodic authoritative velocity updates are enough for this gentle
613 // force while avoiding an NPC sync packet on every tick.
614 if ((Main.GameUpdateCount + (ulong)target.whoAmI) % 10UL == 0UL)
615 target.netUpdate = true;
616 }
617 }
618
561 619 private static int CalculateDeathDomainSlashDamage(NPC target, DeathNecklace necklace)
562 620 {
563 621 bool bossOrBossPart = target.boss
Modified Items/DeathNecklace.cs +3 -0
@@ -33,6 +33,9 @@ public class DeathNecklace : ModItem, IDeathAltarUpgradeable
33 33 public bool SovereigntyUnlocked { get; private set; }
34 34
35 35 public float DomainRadius => 160f + (RadiusLevel - 1) * 32f;
36 public float DomainAttractionRadius => DomainRadius + 320f + (RadiusLevel - 1) * 32f;
37 public float DomainAttractionStrength => 0.035f + (RadiusLevel - 1) * 0.0075f;
38 public float DomainAttractionMaximumSpeed => 0.75f + (RadiusLevel - 1) * 0.09f;
36 39 public int SpawnInterval => Math.Max(34, 100 - (FrequencyLevel - 1) * 7);
37 40 public float NormalSlashLifeRatio => DamageLevel * 0.01f;
38 41 public float BossSlashLifeRatio => DamageLevel * 0.0001f;
Modified Projectiles/DeathDomainHarvestSlashProjectile.cs +46 -48
@@ -140,9 +140,9 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
140 140 float width = (28f + Mastery * 18f + (IsRequiem ? 12f : 0f)) * Projectile.scale;
141 141 Vector2 center = Projectile.Center - Main.screenPosition;
142 142
143 // Three passes over the exact same tapered silhouette produce one integrated
144 // blade. reveal advances from t=0 to t=1, so the cut travels from one tip
145 // toward the other instead of opening outward from its center.
143 // Three passes over one continuous high-resolution silhouette produce one
144 // integrated blade. reveal stretches it from the starting tip toward the
145 // destination, with no MagicPixel seams between individual segments.
146 146 DrawTaperedBladeLayer(center, rotation, length, width * 1.52f, reveal,
147 147 new Color(92, 0, 22, 0) * (strength * 0.54f));
148 148 DrawTaperedBladeLayer(center, rotation, length, width, reveal,
@@ -155,10 +155,36 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
155 155 Vector2 start = center - direction * length * 0.5f;
156 156 Vector2 cuttingEdge = start + direction * (length * reveal);
157 157 float spark = (8f + Mastery * 5f) * Projectile.scale * strength;
158 DrawSegment(cuttingEdge - normal * spark,
159 cuttingEdge + normal * spark,
160 new Color(255, 12, 52, 0) * strength,
161 1.6f + Mastery);
158
159 DeathDomainPrimitiveTextureSystem.DrawBlade(
160 cuttingEdge - normal * spark,
161 normal.ToRotation(),
162 spark * 2f,
163 1.6f + Mastery,
164 new Color(255, 12, 52, 0) * strength);
165
166 // A second, much longer blood-line races through the black core shortly
167 // after the main blade arrives. It gives the execution a crisp crimson
168 // centre without turning the whole strike into another thick beam.
169 float bloodReveal = Smooth01((progress - 0.08f) / 0.34f);
170 float bloodFade = 1f - Smooth01((progress - 0.64f) / 0.28f);
171 float bloodStrength = bloodReveal * bloodFade;
172 float bloodLength = length * (1.78f + Mastery * 0.28f + (IsRequiem ? 0.18f : 0f));
173 Vector2 bloodStart = center - direction * bloodLength * 0.5f;
174 DeathDomainPrimitiveTextureSystem.DrawBlade(
175 bloodStart,
176 rotation,
177 bloodLength,
178 (6.2f + Mastery * 2.2f) * Projectile.scale,
179 new Color(95, 0, 24, 0) * (bloodStrength * 0.72f),
180 bloodReveal);
181 DeathDomainPrimitiveTextureSystem.DrawBlade(
182 bloodStart,
183 rotation,
184 bloodLength,
185 (1.25f + Mastery * 0.55f) * Projectile.scale,
186 new Color(255, 0, 38, 0) * bloodStrength,
187 bloodReveal);
162 188 }
163 189
164 190 private static void DrawTaperedBladeLayer(Vector2 center, float rotation, float length, float maximumWidth, float reveal, Color color)
@@ -166,32 +192,13 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
166 192 if (maximumWidth <= 0.05f || reveal <= 0f)
167 193 return;
168 194
169 Vector2 direction = rotation.ToRotationVector2();
170 Vector2 start = center - direction * length * 0.5f;
171 const int pieces = 36;
172 int visiblePieces = Math.Clamp((int)Math.Ceiling(reveal * pieces), 1, pieces);
173 for (int piece = 0; piece < visiblePieces; piece++)
174 {
175 float t0 = piece / (float)pieces;
176 float t1 = Math.Min(reveal, (piece + 1f) / pieces);
177 if (t1 <= t0)
178 continue;
179 float middle = (t0 + t1) * 0.5f;
180 float bodyTaper = (float)Math.Pow(Math.Max(0f, Math.Sin(MathHelper.Pi * middle)), 0.64f);
181 float movingTipTaper = Smooth01((reveal - middle) / 0.075f);
182 DrawSegment(
183 start + direction * (length * t0),
184 start + direction * (length * t1 + 0.8f),
185 color,
186 Math.Max(0.15f, maximumWidth * bodyTaper * movingTipTaper));
187 }
195 Vector2 start = center - rotation.ToRotationVector2() * length * 0.5f;
196 DeathDomainPrimitiveTextureSystem.DrawBlade(start, rotation, length, maximumWidth, color, reveal);
188 197 }
189 198
190 199 private void ApplyHarvestDamage()
191 200 {
192 if (Main.netMode == NetmodeID.MultiplayerClient
193 || Projectile.damage <= 0
194 || Projectile.owner < 0
201 if (Projectile.owner < 0
195 202 || Projectile.owner >= Main.maxPlayers
196 203 || TargetIndex < 0
197 204 || TargetIndex >= Main.maxNPCs)
@@ -203,6 +210,16 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
203 210 if (!target.active || target.friendly || target.immortal || target.dontTakeDamage || target.life <= 0)
204 211 return;
205 212
213 bool bossOrBossPart = target.boss
214 || NPCID.Sets.ShouldBeCountedAsBoss[target.type]
215 || target.realLife >= 0 && target.realLife < Main.maxNPCs && Main.npc[target.realLife].boss;
216 target.GetGlobalNPC<MyGlobalNPC>().ApplyDeathDomainHitStop(target, bossOrBossPart ? 1 : 3);
217
218 // Explicit client visuals repeat the timing of the hidden server scheduler.
219 // They only apply the local hit-stop above; damage remains authoritative.
220 if (Main.netMode == NetmodeID.MultiplayerClient || Projectile.damage <= 0)
221 return;
222
206 223 target.GetGlobalNPC<MyGlobalNPC>().RegisterSickleHit(target, Projectile.owner);
207 224 target.lastInteraction = Projectile.owner;
208 225 NPC.HitInfo harvestHit = new()
@@ -281,25 +298,6 @@ public class DeathDomainHarvestSlashProjectile : ModProjectile
281 298 return (126f + mastery * 76f) * scale * variation;
282 299 }
283 300
284 private static void DrawSegment(Vector2 start, Vector2 end, Color color, float width)
285 {
286 Vector2 segment = end - start;
287 if (segment.LengthSquared() < 0.01f || width <= 0.01f)
288 return;
289
290 Texture2D pixel = TextureAssets.MagicPixel.Value;
291 Main.EntitySpriteDraw(
292 pixel,
293 start,
294 null,
295 color,
296 segment.ToRotation(),
297 new Vector2(0f, pixel.Height * 0.5f),
298 new Vector2(segment.Length() / pixel.Width, width / pixel.Height),
299 SpriteEffects.None,
300 0f);
301 }
302
303 301 private static float Smooth01(float value)
304 302 {
305 303 value = MathHelper.Clamp(value, 0f, 1f);
Modified Projectiles/DeathDomainHarvestTelegraphProjectile.cs +3 -36
@@ -1,10 +1,9 @@
1 using DeathMod.Common;
1 2 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 3 using System;
4 4 using System.IO;
5 5 using Terraria;
6 6 using Terraria.DataStructures;
7 using Terraria.GameContent;
8 7 using Terraria.ID;
9 8 using Terraria.ModLoader;
10 9
@@ -106,39 +105,7 @@ public class DeathDomainHarvestTelegraphProjectile : ModProjectile
106 105
107 106 private static void DrawTaperedLine(Vector2 center, float rotation, float length, float maximumWidth, Color color)
108 107 {
109 Vector2 direction = rotation.ToRotationVector2();
110 Vector2 start = center - direction * length * 0.5f;
111 const int pieces = 28;
112 for (int piece = 0; piece < pieces; piece++)
113 {
114 float t0 = piece / (float)pieces;
115 float t1 = (piece + 1f) / pieces;
116 float middle = (t0 + t1) * 0.5f;
117 float taper = (float)Math.Pow(Math.Max(0f, Math.Sin(MathHelper.Pi * middle)), 0.62f);
118 DrawSegment(
119 start + direction * (length * t0),
120 start + direction * (length * t1 + 0.8f),
121 color,
122 Math.Max(0.15f, maximumWidth * taper));
123 }
124 }
125
126 private static void DrawSegment(Vector2 start, Vector2 end, Color color, float width)
127 {
128 Vector2 segment = end - start;
129 if (segment.LengthSquared() < 0.01f)
130 return;
131
132 Texture2D pixel = TextureAssets.MagicPixel.Value;
133 Main.EntitySpriteDraw(
134 pixel,
135 start,
136 null,
137 color,
138 segment.ToRotation(),
139 new Vector2(0f, pixel.Height * 0.5f),
140 new Vector2(segment.Length() / pixel.Width, width / pixel.Height),
141 SpriteEffects.None,
142 0f);
108 Vector2 start = center - rotation.ToRotationVector2() * length * 0.5f;
109 DeathDomainPrimitiveTextureSystem.DrawBlade(start, rotation, length, maximumWidth, color);
143 110 }
144 111 }