返回提交历史
Modified
Items/BloodSickle.cs
+6
-1
Modified
Items/DeathSickle.cs
+1
-0
Modified
Items/DevilSickle.cs
+6
-2
Modified
Items/FrostSickle.cs
+6
-1
Modified
Items/NormalSickle.cs
+45
-11
Modified
Items/SkullSickle.cs
+6
-0
Modified
Items/SoulSickle.cs
+6
-1
Modified
Items/VoidSickle.cs
+6
-2
Modified
Localization/en-US.hjson
+13
-2
Modified
Localization/zh-Hans.hjson
+13
-2
Modified
Projectiles/LifeStealWispProjectile.cs
+28
-8
Modified
Projectiles/SickleSwingProjectile.cs
+391
-88
Modified
README.md
+3
-2
Modified
build.txt
+1
-1
Modified
docs/PLAY_GUIDE.md
+18
-11
XFEstudio/DeathMod
fix: diversify sickle combat animations
b4b9f3f
代码差异
15 个文件
+549
-132
@@ -14,10 +14,15 @@ public class BloodSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.52f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<FrostSickle>();
16
16
public override int AlternateNextSickleType => ModContent.ItemType<DevilSickle>();
17
public override bool SupportsCharge => true;
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.Circular;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.BloodCharge;
18
19
public override int MaximumChargeFrames => 48;
19
20
public override Color SickleColor => new(235, 45, 80);
21
public override Color SickleSecondaryColor => new(255, 145, 175);
20
22
public override int SickleDustType => DustID.Blood;
23
public override float TrailWidth => 13f;
24
public override Vector2 HandleAnchor => new(0.13f, 0.94f);
25
public override float TextureRotationCorrection => 0.82f;
21
26
22
27
public override void SetDefaults()
23
28
{
@@ -14,6 +14,7 @@ public class DeathSickle : NormalSickle
14
14
public override int PrimaryNextSickleType => ItemID.None;
15
15
public override int AlternateNextSickleType => ItemID.None;
16
16
public override bool UsesModernSwing => false;
17
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.None;
17
18
public override Color SickleColor => new(235, 35, 65);
18
19
19
20
public override void SetDefaults()
@@ -14,10 +14,14 @@ public class DevilSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.7f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<SoulSickle>();
16
16
public override int AlternateNextSickleType => ModContent.ItemType<VoidSickle>();
17
public override bool SupportsCharge => true;
18
public override int MaximumChargeFrames => 62;
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.Rising;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.InfernalRush;
19
19
public override Color SickleColor => new(255, 125, 25);
20
public override Color SickleSecondaryColor => new(255, 235, 85);
20
21
public override int SickleDustType => DustID.Torch;
22
public override float TrailWidth => 16f;
23
public override Vector2 HandleAnchor => new(0.13f, 0.93f);
24
public override float TextureRotationCorrection => 0.79f;
21
25
22
26
public override void SetDefaults()
23
27
{
@@ -14,10 +14,15 @@ public class FrostSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.65f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<VoidSickle>();
16
16
public override int AlternateNextSickleType => ModContent.ItemType<SoulSickle>();
17
public override bool SupportsCharge => true;
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.CrossCut;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.FrostCharge;
18
19
public override int MaximumChargeFrames => 58;
19
20
public override Color SickleColor => new(105, 195, 255);
21
public override Color SickleSecondaryColor => new(235, 255, 255);
20
22
public override int SickleDustType => DustID.IceTorch;
23
public override float TrailWidth => 14f;
24
public override Vector2 HandleAnchor => new(0.18f, 0.93f);
25
public override float TextureRotationCorrection => 0.80f;
21
26
22
27
public override void SetDefaults()
23
28
{
@@ -13,6 +13,29 @@ using Terraria.ModLoader.IO;
13
13
14
14
namespace DeathMod.Items;
15
15
16
public enum SickleSwingStyle : byte
17
{
18
Diagonal,
19
Overhead,
20
Circular,
21
Rising,
22
CrossCut,
23
SoulOrbit,
24
VoidOrbit
25
}
26
27
public enum SickleAlternateAttackStyle : byte
28
{
29
None,
30
HarvestDash,
31
BoneRush,
32
BloodCharge,
33
InfernalRush,
34
FrostCharge,
35
SoulCharge,
36
VoidStep
37
}
38
16
39
public class NormalSickle : ModItem, IDeathAltarUpgradeable
17
40
{
18
41
public const int BaseUseTime = 25;
@@ -67,15 +90,24 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
67
90
public virtual int PrimaryNextSickleType => ModContent.ItemType<SkullSickle>();
68
91
public virtual int AlternateNextSickleType => ModContent.ItemType<BloodSickle>();
69
92
public virtual bool UsesModernSwing => true;
70
public virtual bool SupportsCharge => false;
93
public virtual SickleSwingStyle SwingStyle => SickleSwingStyle.Diagonal;
94
public virtual SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.HarvestDash;
71
95
public virtual int MaximumChargeFrames => 55 + SickleTier * 8;
72
96
public virtual int SickleReach => 74 + SickleTier * 10;
73
97
public virtual float SickleCollisionWidth => 18f + SickleTier * 3f;
74
98
public virtual int ChargedProjectileBonus => SickleTier >= 2 ? 2 : 1;
75
99
public virtual Color SickleColor => new(80, 205, 225);
100
public virtual Color SickleSecondaryColor => new(210, 250, 255);
76
101
public virtual int SickleDustType => DustID.AncientLight;
102
public virtual float TrailWidth => 8f;
103
public virtual Vector2 HandleAnchor => new(0.06f, 0.95f);
104
public virtual float TextureRotationCorrection => 0.98f;
77
105
78
106
public bool SupportsProjectileCount => AttackProjectileType > ProjectileID.None;
107
public bool HasAlternateAttack => AlternateAttackStyle != SickleAlternateAttackStyle.None;
108
public bool SupportsCharge => AlternateAttackStyle is SickleAlternateAttackStyle.BloodCharge
109
or SickleAlternateAttackStyle.FrostCharge
110
or SickleAlternateAttackStyle.SoulCharge;
79
111
public double DamageMultiplier => Math.Pow(4.2d, (DamageLevel - 1d) / (MaxDamageLevel - 1d));
80
112
public int ScaledBaseDamage => GetScaledDamage(DamageLevel);
81
113
public int AttackUseTime => BaseUseTime - (AttackSpeedLevel - 1);
@@ -105,7 +137,7 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
105
137
Item.autoReuse = true;
106
138
Item.noUseGraphic = UsesModernSwing;
107
139
Item.noMelee = UsesModernSwing;
108
Item.channel = false;
140
Item.channel = SupportsCharge;
109
141
Item.shoot = UsesModernSwing ? ModContent.ProjectileType<SickleSwingProjectile>() : AttackProjectileType;
110
142
Item.shootSpeed = AttackProjectileSpeed;
111
143
ApplyDynamicStats();
@@ -130,7 +162,7 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
130
162
knockback.Flat += KnockbackBonus;
131
163
}
132
164
133
public override bool AltFunctionUse(Player player) => UsesModernSwing && SupportsCharge;
165
public override bool AltFunctionUse(Player player) => UsesModernSwing && HasAlternateAttack;
134
166
135
167
public override bool CanUseItem(Player player)
136
168
{
@@ -140,10 +172,11 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
140
172
if (player.ownedProjectileCounts[ModContent.ProjectileType<SickleSwingProjectile>()] > 0)
141
173
return false;
142
174
143
bool charging = player.altFunctionUse == 2 && SupportsCharge;
175
bool alternateAttack = player.altFunctionUse == 2 && HasAlternateAttack;
176
bool charging = alternateAttack && SupportsCharge;
144
177
Item.channel = charging;
145
Item.useTime = charging ? 10 : BaseUseTime;
146
Item.useAnimation = charging ? 10 : BaseUseTime;
178
Item.useTime = alternateAttack ? 10 : BaseUseTime;
179
Item.useAnimation = alternateAttack ? 10 : BaseUseTime;
147
180
return true;
148
181
}
149
182
@@ -155,8 +188,8 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
155
188
return false;
156
189
}
157
190
158
bool chargedAttack = player.altFunctionUse == 2 && SupportsCharge;
159
int swingDirection = chargedAttack
191
bool alternateAttack = player.altFunctionUse == 2 && HasAlternateAttack;
192
int swingDirection = alternateAttack
160
193
? player.direction
161
194
: player.GetModPlayer<MyPlayer>().TakeNextSwingDirection(player.direction);
162
195
Vector2 aim = velocity.SafeNormalize(Vector2.UnitX * player.direction);
@@ -169,7 +202,7 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
169
202
knockback,
170
203
player.whoAmI,
171
204
swingDirection,
172
chargedAttack ? 1f : 0f,
205
alternateAttack ? 1f : 0f,
173
206
Item.type);
174
207
return false;
175
208
}
@@ -198,9 +231,10 @@ public class NormalSickle : ModItem, IDeathAltarUpgradeable
198
231
{
199
232
OverrideColor = GetGradientColor()
200
233
});
201
if (SupportsCharge)
234
if (HasAlternateAttack)
202
235
{
203
tooltips.Add(new TooltipLine(Mod, "DeathModCharge", Language.GetTextValue("Mods.DeathMod.UI.ChargeHint"))
236
string alternateHint = Language.GetTextValue($"Mods.DeathMod.UI.AlternateHints.{AlternateAttackStyle}");
237
tooltips.Add(new TooltipLine(Mod, "DeathModAlternateAttack", alternateHint)
204
238
{
205
239
OverrideColor = SickleColor
206
240
});
@@ -14,8 +14,14 @@ public class SkullSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.58f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<DevilSickle>();
16
16
public override int AlternateNextSickleType => ModContent.ItemType<FrostSickle>();
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.Overhead;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.BoneRush;
17
19
public override Color SickleColor => new(80, 235, 255);
20
public override Color SickleSecondaryColor => new(235, 245, 220);
18
21
public override int SickleDustType => DustID.DungeonSpirit;
22
public override float TrailWidth => 10f;
23
public override Vector2 HandleAnchor => new(0.25f, 0.94f);
24
public override float TextureRotationCorrection => 0.97f;
19
25
20
26
public override void SetDefaults()
21
27
{
@@ -14,10 +14,15 @@ public class SoulSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.82f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<DeathSickle>();
16
16
public override int AlternateNextSickleType => ItemID.None;
17
public override bool SupportsCharge => true;
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.SoulOrbit;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.SoulCharge;
18
19
public override int MaximumChargeFrames => 72;
19
20
public override Color SickleColor => new(120, 100, 255);
21
public override Color SickleSecondaryColor => new(75, 245, 255);
20
22
public override int SickleDustType => DustID.AncientLight;
23
public override float TrailWidth => 18f;
24
public override Vector2 HandleAnchor => new(0.20f, 0.93f);
25
public override float TextureRotationCorrection => 0.84f;
21
26
22
27
public override void SetDefaults()
23
28
{
@@ -14,11 +14,15 @@ public class VoidSickle : NormalSickle
14
14
public override float ProjectileDamageFactor => 0.88f;
15
15
public override int PrimaryNextSickleType => ModContent.ItemType<DeathSickle>();
16
16
public override int AlternateNextSickleType => ItemID.None;
17
public override bool SupportsCharge => true;
18
public override int MaximumChargeFrames => 78;
17
public override SickleSwingStyle SwingStyle => SickleSwingStyle.VoidOrbit;
18
public override SickleAlternateAttackStyle AlternateAttackStyle => SickleAlternateAttackStyle.VoidStep;
19
19
public override int ChargedProjectileBonus => 1;
20
20
public override Color SickleColor => new(190, 55, 255);
21
public override Color SickleSecondaryColor => new(255, 85, 235);
21
22
public override int SickleDustType => DustID.Shadowflame;
23
public override float TrailWidth => 20f;
24
public override Vector2 HandleAnchor => new(0.13f, 0.93f);
25
public override float TextureRotationCorrection => 0.89f;
22
26
23
27
public override void SetDefaults()
24
28
{
@@ -18,7 +18,7 @@ Mods: {
18
18
19
19
DevilSickle: {
20
20
DisplayName: Infernal Sickle
21
Tooltip: Releases accelerating molten crescents; supports a charged right-click heavy slash
21
Tooltip: Releases accelerating molten crescents; right-click for an instant rushing infernal spin
22
22
}
23
23
24
24
FrostSickle: {
@@ -33,7 +33,7 @@ Mods: {
33
33
34
34
VoidSickle: {
35
35
DisplayName: Void Sickle
36
Tooltip: Releases void arcs that grow into gravitational rifts; supports a charged right-click heavy slash
36
Tooltip: Releases void arcs that grow into gravitational rifts; right-click for a Void Step orbit slash
37
37
}
38
38
39
39
DeathSickle: {
@@ -114,6 +114,17 @@ Mods: {
114
114
Projectiles {2} | Armor penetration {3} | Life steal {4}% | Hit cooldown {7}f
115
115
'''
116
116
ChargeHint: Right-click to charge with a separate soul-gathering pose, then release a high-multiplier heavy slash and empowered projectile pattern
117
118
AlternateHints: {
119
HarvestDash: Right-click: instantly dash toward the cursor with a forward diagonal slash
120
BoneRush: Right-click: rush forward with a non-charged bone-wraith thrust
121
BloodCharge: Hold right-click to charge, then release a circular Blood Covenant execution
122
InfernalRush: Right-click: rush forward with a non-charged infernal spin
123
FrostCharge: Hold right-click to charge, then release a two-stage frost cross-cut
124
SoulCharge: Hold right-click to charge, then release a multi-orbit soul reaping slash
125
VoidStep: Right-click: perform a non-charged Void Step and surrounding rift slash
126
}
127
117
128
WingStats: Flight {0}f | Speed {1} | Acceleration {2} | Vertical power {3} | Infinite flight: {4}
118
129
RobeStats: Defense {0} | Life +{1} | Life regeneration +{2} | Debuff immunities {3}/{4}
119
130
AltarHint: Right-click a Death Altar to open its modification interface
@@ -18,7 +18,7 @@ Mods: {
18
18
19
19
DevilSickle: {
20
20
DisplayName: 炼狱镰
21
Tooltip: 释放不断加速的熔火弧刃;支持右键蓄力重斩
21
Tooltip: 释放不断加速的熔火弧刃;右键施展无需蓄力的炼狱突进旋斩
22
22
}
23
23
24
24
FrostSickle: {
@@ -33,7 +33,7 @@ Mods: {
33
33
34
34
VoidSickle: {
35
35
DisplayName: 虚空镰刀
36
Tooltip: 释放成长为引力裂隙的虚空弧;支持右键蓄力重斩
36
Tooltip: 释放成长为引力裂隙的虚空弧;右键施展虚空踏步环斩
37
37
}
38
38
39
39
DeathSickle: {
@@ -114,6 +114,17 @@ Mods: {
114
114
弹幕 {2} | 穿甲 {3} | 吸血 {4}% | 伤害间隔 {7}帧
115
115
'''
116
116
ChargeHint: 右键蓄力:使用独立聚魂动作,松开后施展高倍率重斩与强化弹幕
117
118
AlternateHints: {
119
HarvestDash: 右键:立即向准星方向突进并施展前冲斜斩
120
BoneRush: 右键:无需蓄力,以亡骨镰向前突刺追魂
121
BloodCharge: 右键按住蓄力,松开后施展环身血契处决斩
122
InfernalRush: 右键:无需蓄力,突进并施展炼狱旋斩
123
FrostCharge: 右键按住蓄力,松开后施展两段冰魂交叉斩
124
SoulCharge: 右键按住蓄力,松开后施展多圈灵魂回旋斩
125
VoidStep: 右键:无需蓄力,施展虚空踏步与环身裂隙斩
126
}
127
117
128
WingStats: 飞行 {0}帧 | 速度 {1} | 加速度 {2} | 垂直能力 {3} | 无限飞行:{4}
118
129
RobeStats: 防御 {0} | 生命 +{1} | 生命回复 +{2} | 减益免疫 {3}/{4}
119
130
AltarHint: 在死神祭坛右键打开改造界面
@@ -55,6 +55,7 @@ public class LifeStealWispProjectile : ModProjectile
55
55
Projectile.timeLeft = 90;
56
56
Projectile.penetrate = -1;
57
57
Projectile.netImportant = false;
58
Projectile.extraUpdates = 1;
58
59
}
59
60
60
61
public override void AI()
@@ -69,10 +70,14 @@ public class LifeStealWispProjectile : ModProjectile
69
70
Projectile.localAI[0]++;
70
71
float age = Projectile.localAI[0];
71
72
Vector2 toPlayer = player.Center - Projectile.Center;
72
float speed = MathHelper.Lerp(4f, 15f + LifeStealLevel, MathHelper.Clamp(age / 42f, 0f, 1f));
73
Vector2 desired = toPlayer.SafeNormalize(Vector2.UnitY) * speed;
74
float swirl = (float)System.Math.Sin(age * 0.24f + Projectile.ai[1]) * (0.16f - MathHelper.Clamp(age / 420f, 0f, 0.12f));
75
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desired.RotatedBy(swirl), 0.11f + LifeStealLevel * 0.012f);
73
float approach = MathHelper.Clamp(age / 58f, 0f, 1f);
74
float speed = MathHelper.Lerp(3.5f, 13f + LifeStealLevel, approach);
75
Vector2 radialDirection = toPlayer.SafeNormalize(Vector2.UnitY);
76
Vector2 tangent = radialDirection.RotatedBy(MathHelper.PiOver2)
77
* (float)System.Math.Sin(age * 0.2f + Projectile.ai[1])
78
* MathHelper.Lerp(5.5f + LifeStealLevel, 0.7f, approach);
79
Vector2 desired = radialDirection * speed + tangent;
80
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desired, 0.085f + LifeStealLevel * 0.01f);
76
81
Projectile.rotation = Projectile.velocity.ToRotation();
77
82
78
83
Color color = Color.Lerp(new Color(215, 20, 45), new Color(255, 105, 195), LifeStealLevel / 5f);
@@ -83,7 +88,7 @@ public class LifeStealWispProjectile : ModProjectile
83
88
dust.noGravity = true;
84
89
}
85
90
86
if (toPlayer.LengthSquared() > 18f * 18f)
91
if (toPlayer.LengthSquared() > 16f * 16f)
87
92
return;
88
93
89
94
for (int index = 0; index < 3 + LifeStealLevel * 2; index++)
@@ -102,10 +107,25 @@ public class LifeStealWispProjectile : ModProjectile
102
107
for (int index = trailLength; index >= 1; index--)
103
108
{
104
109
Vector2 position = Projectile.oldPos[index] + Projectile.Size / 2f - Main.screenPosition;
105
float scale = (trailLength - index + 1f) / trailLength;
106
Main.EntitySpriteDraw(pixel, position, null, color * scale * 0.55f, Projectile.rotation, new Vector2(0.5f), new Vector2(5f + LifeStealLevel, 2f) * scale, SpriteEffects.None);
110
float strength = (trailLength - index + 1f) / trailLength;
111
float size = (1.6f + LifeStealLevel * 0.34f) * strength;
112
Main.EntitySpriteDraw(pixel, position, null, color * strength * 0.48f,
113
MathHelper.PiOver4 + index * 0.18f, new Vector2(0.5f), new Vector2(size), SpriteEffects.None);
114
if (LifeStealLevel >= 3 && index % 2 == 0)
115
{
116
Vector2 orbitOffset = (Projectile.ai[1] + index * 1.7f).ToRotationVector2() * (2f + LifeStealLevel * 0.45f);
117
Main.EntitySpriteDraw(pixel, position + orbitOffset, null, Color.White * strength * 0.32f,
118
MathHelper.PiOver4, new Vector2(0.5f), new Vector2(size * 0.45f), SpriteEffects.None);
119
}
107
120
}
108
Main.EntitySpriteDraw(pixel, Projectile.Center - Main.screenPosition, null, color * 0.95f, Projectile.rotation, new Vector2(0.5f), new Vector2(7f + LifeStealLevel * 1.4f, 3f + LifeStealLevel * 0.5f), SpriteEffects.None);
121
122
Vector2 center = Projectile.Center - Main.screenPosition;
123
float pulse = 1f + (float)System.Math.Sin(Projectile.localAI[0] * 0.24f) * 0.16f;
124
float coreSize = (3.5f + LifeStealLevel * 0.65f) * pulse;
125
Main.EntitySpriteDraw(pixel, center, null, color * 0.9f, MathHelper.PiOver4,
126
new Vector2(0.5f), new Vector2(coreSize), SpriteEffects.None);
127
Main.EntitySpriteDraw(pixel, center, null, Color.White * 0.72f, -MathHelper.PiOver4,
128
new Vector2(0.5f), new Vector2(coreSize * 0.38f), SpriteEffects.None);
109
129
return false;
110
130
}
111
131
}
@@ -2,6 +2,7 @@ using DeathMod.Items;
2
2
using Microsoft.Xna.Framework;
3
3
using Microsoft.Xna.Framework.Graphics;
4
4
using System;
5
using System.IO;
5
6
using Terraria;
6
7
using Terraria.Audio;
7
8
using Terraria.GameContent;
@@ -12,24 +13,33 @@ namespace DeathMod.Projectiles;
12
13
13
14
public class SickleSwingProjectile : ModProjectile
14
15
{
15
private const int HistoryLength = 12;
16
private const int HistoryLength = 16;
16
17
17
18
private readonly float[] angleHistory = new float[HistoryLength];
19
private readonly Vector2[] gripHistory = new Vector2[HistoryLength];
20
private readonly bool[] flipHistory = new bool[HistoryLength];
18
21
private float aimRotation;
19
22
private float weaponAngle;
20
23
private float chargeRatio;
24
private Vector2 gripPosition;
21
25
private int chargeTimer;
22
26
private int swingTimer;
27
private int actionSegment;
28
private int historySegment = -1;
29
private bool weaponFlipped;
23
30
private bool initialized;
24
31
private bool spawnedAttack;
25
32
private bool fullChargeFeedback;
33
private bool dashApplied;
34
private bool alternateDamageApplied;
35
private bool canDamage;
26
36
27
37
public override string Texture => "Terraria/Images/Projectile_0";
28
38
29
39
private int ItemType => (int)Projectile.ai[2];
30
40
private int SwingDirection => Math.Sign(Projectile.ai[0]) == 0 ? 1 : Math.Sign(Projectile.ai[0]);
31
private bool IsCharging => Projectile.ai[1] == 1f;
32
private bool IsHeavySlash => Projectile.ai[1] == 2f;
41
private bool IsAlternateAttack => Projectile.ai[1] >= 1f;
42
private bool IsReleasedCharge => Projectile.ai[1] == 2f;
33
43
34
44
public override void SetDefaults()
35
45
{
@@ -50,6 +60,20 @@ public class SickleSwingProjectile : ModProjectile
50
60
51
61
public override bool ShouldUpdatePosition() => false;
52
62
63
public override bool? CanDamage() => canDamage ? null : false;
64
65
public override void SendExtraAI(BinaryWriter writer)
66
{
67
writer.Write(chargeRatio);
68
writer.Write((byte)actionSegment);
69
}
70
71
public override void ReceiveExtraAI(BinaryReader reader)
72
{
73
chargeRatio = MathHelper.Clamp(reader.ReadSingle(), 0f, 1f);
74
actionSegment = reader.ReadByte();
75
}
76
53
77
public override void AI()
54
78
{
55
79
if (!TryGetSickle(out Player player, out NormalSickle sickle))
@@ -59,13 +83,7 @@ public class SickleSwingProjectile : ModProjectile
59
83
}
60
84
61
85
if (!initialized)
62
{
63
initialized = true;
64
aimRotation = Projectile.velocity.SafeNormalize(Vector2.UnitX * player.direction).ToRotation();
65
weaponAngle = aimRotation;
66
Array.Fill(angleHistory, weaponAngle);
67
SoundEngine.PlaySound(IsCharging ? SoundID.Item15 : SoundID.Item1, player.Center);
68
}
86
InitializeSwing(player, sickle);
69
87
70
88
Projectile.timeLeft = 2;
71
89
Projectile.Center = player.MountedCenter;
@@ -73,32 +91,35 @@ public class SickleSwingProjectile : ModProjectile
73
91
player.itemTime = 2;
74
92
player.itemAnimation = 2;
75
93
player.ChangeDir(Projectile.velocity.X >= 0f ? 1 : -1);
94
gripPosition = player.MountedCenter;
95
canDamage = false;
76
96
77
if (IsCharging)
97
if (!IsAlternateAttack)
98
UpdatePrimarySlash(player, sickle);
99
else if (sickle.SupportsCharge && !IsReleasedCharge)
78
100
UpdateCharge(player, sickle);
101
else if (sickle.SupportsCharge)
102
UpdateChargedSlash(player, sickle);
79
103
else
80
UpdateSlash(player, sickle, IsHeavySlash);
104
UpdateInstantAlternate(player, sickle);
81
105
82
PushAngleHistory();
106
UpdateHistory();
83
107
UpdatePlayerArm(player);
84
108
SpawnMotionEffects(player, sickle);
85
109
}
86
110
87
111
public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
88
112
{
89
if (IsCharging || !TryGetSickle(out Player player, out NormalSickle sickle))
113
if (!canDamage || !TryGetSickle(out _, out NormalSickle sickle))
90
114
return false;
91
115
92
Vector2 start = player.MountedCenter + weaponAngle.ToRotationVector2() * 10f;
93
Vector2 end = player.MountedCenter + weaponAngle.ToRotationVector2() * sickle.SickleReach;
116
Vector2 start = gripPosition + weaponAngle.ToRotationVector2() * 8f;
117
Vector2 end = gripPosition + weaponAngle.ToRotationVector2() * sickle.SickleReach;
94
118
float collisionPoint = 0f;
119
float widthMultiplier = IsAlternateAttack ? 1.35f : 1f;
95
120
return Collision.CheckAABBvLineCollision(
96
targetHitbox.TopLeft(),
97
targetHitbox.Size(),
98
start,
99
end,
100
sickle.SickleCollisionWidth * (IsHeavySlash ? 1.4f : 1f),
101
ref collisionPoint);
121
targetHitbox.TopLeft(), targetHitbox.Size(), start, end,
122
sickle.SickleCollisionWidth * widthMultiplier, ref collisionPoint);
102
123
}
103
124
104
125
public override bool PreDraw(ref Color lightColor)
@@ -106,65 +127,127 @@ public class SickleSwingProjectile : ModProjectile
106
127
if (!TryGetSickle(out Player player, out NormalSickle sickle))
107
128
return false;
108
129
130
bool activelyCharging = IsAlternateAttack && sickle.SupportsCharge && !IsReleasedCharge;
131
if (activelyCharging)
132
DrawChargeRing(player, sickle);
133
else
134
DrawSlashTrail(sickle);
135
109
136
Texture2D texture = TextureAssets.Item[ItemType].Value;
110
Vector2 origin = new(texture.Width * 0.10f, texture.Height * 0.90f);
111
float textureAxisCorrection = 0.92f;
112
float scale = 0.82f + sickle.SickleTier * 0.075f + (IsHeavySlash ? chargeRatio * 0.12f : 0f);
113
int trailCount = Math.Min(HistoryLength - 1, 2 + sickle.SickleTier * 2 + (IsHeavySlash ? 2 : 0));
137
float scale = 0.82f + sickle.SickleTier * 0.075f + (IsReleasedCharge ? chargeRatio * 0.12f : 0f);
138
int afterimageCount = Math.Min(HistoryLength - 1, 2 + sickle.SickleTier * 2 + (IsAlternateAttack ? 2 : 0));
114
139
115
for (int index = trailCount; index >= 1; index--)
140
for (int index = afterimageCount; index >= 1; index--)
116
141
{
117
float opacity = (trailCount - index + 1f) / (trailCount + 1f) * 0.28f;
118
Color trailColor = sickle.SickleColor with { A = 0 };
119
Main.EntitySpriteDraw(
120
texture,
121
player.MountedCenter - Main.screenPosition,
122
null,
123
trailColor * opacity,
124
angleHistory[index] + textureAxisCorrection,
125
origin,
126
scale * (1f + index * 0.012f),
127
SpriteEffects.None);
142
float opacity = (afterimageCount - index + 1f) / (afterimageCount + 1f) * 0.22f;
143
DrawWeapon(texture, sickle, gripHistory[index], angleHistory[index], flipHistory[index],
144
sickle.SickleColor with { A = 0 }, opacity, scale * (1f + index * 0.01f));
128
145
}
129
146
130
if (IsCharging)
131
DrawChargeRing(player, sickle);
132
133
Color auraColor = sickle.SickleColor with { A = 0 };
134
147
int auraCopies = 1 + sickle.SickleTier / 2;
135
148
for (int index = 0; index < auraCopies; index++)
136
149
{
137
150
float angle = Main.GlobalTimeWrappedHourly * 4f + index * MathHelper.TwoPi / auraCopies;
138
151
Vector2 offset = angle.ToRotationVector2() * (1.5f + chargeRatio * 2.5f);
139
Main.EntitySpriteDraw(
140
texture,
141
player.MountedCenter + offset - Main.screenPosition,
142
null,
143
auraColor * (0.16f + chargeRatio * 0.12f),
144
weaponAngle + textureAxisCorrection,
145
origin,
146
scale,
147
SpriteEffects.None);
152
DrawWeapon(texture, sickle, gripPosition + offset, weaponAngle, weaponFlipped,
153
sickle.SickleColor with { A = 0 }, 0.13f + chargeRatio * 0.1f, scale);
148
154
}
149
155
150
Main.EntitySpriteDraw(
151
texture,
152
player.MountedCenter - Main.screenPosition,
153
null,
154
lightColor,
155
weaponAngle + textureAxisCorrection,
156
origin,
157
scale,
158
SpriteEffects.None);
156
DrawWeapon(texture, sickle, gripPosition, weaponAngle, weaponFlipped, lightColor, 1f, scale);
159
157
return false;
160
158
}
161
159
160
private void InitializeSwing(Player player, NormalSickle sickle)
161
{
162
initialized = true;
163
aimRotation = Projectile.velocity.SafeNormalize(Vector2.UnitX * player.direction).ToRotation();
164
weaponAngle = aimRotation;
165
gripPosition = player.MountedCenter;
166
weaponFlipped = SwingDirection < 0;
167
ResetHistory();
168
169
SoundStyle sound = !IsAlternateAttack
170
? SoundID.Item1
171
: sickle.SupportsCharge
172
? SoundID.Item15
173
: sickle.AlternateAttackStyle switch
174
{
175
SickleAlternateAttackStyle.BoneRush => SoundID.Item71,
176
SickleAlternateAttackStyle.InfernalRush => SoundID.Item74,
177
SickleAlternateAttackStyle.VoidStep => SoundID.Item8,
178
_ => SoundID.Item71
179
};
180
SoundEngine.PlaySound(sound, player.Center);
181
}
182
183
private void UpdatePrimarySlash(Player player, NormalSickle sickle)
184
{
185
swingTimer++;
186
float durationMultiplier = sickle.SwingStyle switch
187
{
188
SickleSwingStyle.Overhead => 1.08f,
189
SickleSwingStyle.Circular => 1.24f,
190
SickleSwingStyle.Rising => 1.12f,
191
SickleSwingStyle.CrossCut => 1.26f,
192
SickleSwingStyle.SoulOrbit => 1.36f,
193
SickleSwingStyle.VoidOrbit => 1.45f,
194
_ => 1f
195
};
196
int duration = Math.Max(12, (int)Math.Round(sickle.AttackUseTime * durationMultiplier));
197
float progress = MathHelper.Clamp(swingTimer / (float)duration, 0f, 1f);
198
199
switch (sickle.SwingStyle)
200
{
201
case SickleSwingStyle.Overhead:
202
SetArc(aimRotation, -1.72f, 1.02f, SmoothStep(progress), SwingDirection, 0);
203
gripPosition += new Vector2(0f, -3f);
204
break;
205
case SickleSwingStyle.Circular:
206
SetArc(aimRotation, -2.85f, 2.35f, SmoothStep(progress), SwingDirection, 0);
207
break;
208
case SickleSwingStyle.Rising:
209
SetArc(aimRotation, 2.18f, -1.42f, EaseOutSine(progress), SwingDirection, 0);
210
gripPosition += aimRotation.ToRotationVector2().RotatedBy(MathHelper.PiOver2 * SwingDirection) * 3f;
211
break;
212
case SickleSwingStyle.CrossCut:
213
UpdateCrossCut(progress, aimRotation, SwingDirection, false);
214
break;
215
case SickleSwingStyle.SoulOrbit:
216
SetArc(aimRotation, -3.05f, 2.72f, SmoothStep(progress), SwingDirection, 0);
217
gripPosition += weaponAngle.ToRotationVector2().RotatedBy(MathHelper.PiOver2) * (float)Math.Sin(progress * MathHelper.Pi) * 5f;
218
break;
219
case SickleSwingStyle.VoidOrbit:
220
SetArc(aimRotation, -MathHelper.Pi, MathHelper.Pi, SmoothStep(progress), SwingDirection, 0);
221
gripPosition += aimRotation.ToRotationVector2() * (float)Math.Sin(progress * MathHelper.Pi) * 6f;
222
break;
223
default:
224
SetArc(aimRotation, -2.08f, 1.2f, EaseOutCubic(progress), SwingDirection, 0);
225
break;
226
}
227
228
canDamage = progress is > 0.1f and < 0.96f;
229
float releasePoint = sickle.SwingStyle is SickleSwingStyle.Circular or SickleSwingStyle.SoulOrbit ? 0.48f : 0.38f;
230
TrySpawnAttack(player, sickle, progress, releasePoint, false);
231
if (progress >= 1f)
232
Projectile.Kill();
233
}
234
162
235
private void UpdateCharge(Player player, NormalSickle sickle)
163
236
{
164
237
chargeTimer++;
165
238
chargeRatio = MathHelper.Clamp(chargeTimer / (float)sickle.MaximumChargeFrames, 0f, 1f);
166
239
float breathe = (float)Math.Sin(chargeTimer * 0.12f) * (0.045f + chargeRatio * 0.035f);
167
weaponAngle = aimRotation - player.direction * MathHelper.Lerp(1.05f, 1.35f, chargeRatio) + breathe;
240
float poseOffset = sickle.AlternateAttackStyle switch
241
{
242
SickleAlternateAttackStyle.BloodCharge => 1.22f,
243
SickleAlternateAttackStyle.FrostCharge => 0.9f,
244
SickleAlternateAttackStyle.SoulCharge => 1.52f,
245
_ => 1.1f
246
};
247
weaponAngle = aimRotation - player.direction * MathHelper.Lerp(poseOffset, poseOffset + 0.28f, chargeRatio) + breathe;
248
weaponFlipped = player.direction < 0;
249
gripPosition -= aimRotation.ToRotationVector2() * (5f + chargeRatio * 4f);
250
actionSegment = 0;
168
251
169
252
if (chargeRatio >= 1f && !fullChargeFeedback)
170
253
{
@@ -178,7 +261,11 @@ public class SickleSwingProjectile : ModProjectile
178
261
}
179
262
}
180
263
181
if (player.channel && !player.noItems && !player.CCed)
264
if (Projectile.owner != Main.myPlayer)
265
return;
266
267
bool holdingAlternateUse = player.channel || player.controlUseTile || Main.mouseRight;
268
if (chargeTimer <= 8 || holdingAlternateUse && !player.noItems && !player.CCed)
182
269
return;
183
270
184
271
Projectile.ai[1] = 2f;
@@ -186,35 +273,149 @@ public class SickleSwingProjectile : ModProjectile
186
273
spawnedAttack = false;
187
274
Projectile.damage = Math.Max(1, (int)(Projectile.originalDamage * MathHelper.Lerp(1.35f, 2.45f, chargeRatio)));
188
275
Projectile.netUpdate = true;
189
SoundEngine.PlaySound(SoundID.Item71, player.Center);
276
SoundEngine.PlaySound(sickle.AlternateAttackStyle == SickleAlternateAttackStyle.FrostCharge ? SoundID.Item28 : SoundID.Item71, player.Center);
277
}
278
279
private void UpdateChargedSlash(Player player, NormalSickle sickle)
280
{
281
swingTimer++;
282
int duration = sickle.AlternateAttackStyle switch
283
{
284
SickleAlternateAttackStyle.BloodCharge => Math.Max(30, (int)Math.Round(sickle.AttackUseTime * 1.8f)),
285
SickleAlternateAttackStyle.FrostCharge => Math.Max(32, (int)Math.Round(sickle.AttackUseTime * 1.9f)),
286
SickleAlternateAttackStyle.SoulCharge => Math.Max(38, (int)Math.Round(sickle.AttackUseTime * 2.15f)),
287
_ => 30
288
};
289
float progress = MathHelper.Clamp(swingTimer / (float)duration, 0f, 1f);
290
291
switch (sickle.AlternateAttackStyle)
292
{
293
case SickleAlternateAttackStyle.FrostCharge:
294
UpdateCrossCut(progress, aimRotation, player.direction, true);
295
gripPosition += new Vector2(0f, -2f);
296
break;
297
case SickleAlternateAttackStyle.SoulCharge:
298
SetArc(aimRotation, -3.35f, 5.05f, SmoothStep(progress), player.direction, 0);
299
gripPosition += weaponAngle.ToRotationVector2().RotatedBy(MathHelper.PiOver2) * (6f + chargeRatio * 4f) * (float)Math.Sin(progress * MathHelper.Pi);
300
break;
301
default:
302
SetArc(aimRotation, -3.0f, 2.62f, SmoothStep(SmoothStep(progress)), player.direction, 0);
303
gripPosition += aimRotation.ToRotationVector2() * (float)Math.Sin(progress * MathHelper.Pi) * 5f;
304
break;
305
}
306
307
canDamage = progress is > 0.08f and < 0.97f;
308
float releasePoint = sickle.AlternateAttackStyle == SickleAlternateAttackStyle.SoulCharge ? 0.58f : 0.5f;
309
TrySpawnAttack(player, sickle, progress, releasePoint, true);
310
if (progress >= 1f)
311
Projectile.Kill();
190
312
}
191
313
192
private void UpdateSlash(Player player, NormalSickle sickle, bool heavySlash)
314
private void UpdateInstantAlternate(Player player, NormalSickle sickle)
193
315
{
194
316
swingTimer++;
195
int duration = heavySlash
196
? Math.Max(26, (int)Math.Round(sickle.AttackUseTime * 1.65f))
197
: sickle.AttackUseTime;
317
int duration = sickle.AlternateAttackStyle switch
318
{
319
SickleAlternateAttackStyle.HarvestDash => 20,
320
SickleAlternateAttackStyle.BoneRush => 24,
321
SickleAlternateAttackStyle.InfernalRush => 30,
322
SickleAlternateAttackStyle.VoidStep => 34,
323
_ => 22
324
};
198
325
float progress = MathHelper.Clamp(swingTimer / (float)duration, 0f, 1f);
199
float easedProgress = heavySlash
200
? SmoothStep(SmoothStep(progress))
201
: 1f - (float)Math.Pow(1f - progress, 3f);
202
float startAngle = aimRotation - SwingDirection * (heavySlash ? 2.85f : 2.05f);
203
float endAngle = aimRotation + SwingDirection * (heavySlash ? 1.55f : 1.2f);
204
weaponAngle = MathHelper.Lerp(startAngle, endAngle, easedProgress);
205
206
float releasePoint = heavySlash ? 0.52f : 0.38f;
207
if (!spawnedAttack && progress >= releasePoint)
326
327
if (!alternateDamageApplied)
208
328
{
209
spawnedAttack = true;
210
if (Projectile.owner == Main.myPlayer)
211
sickle.SpawnAttackProjectiles(player, Projectile, chargeRatio, heavySlash);
329
alternateDamageApplied = true;
330
float multiplier = sickle.AlternateAttackStyle switch
331
{
332
SickleAlternateAttackStyle.BoneRush => 1.2f,
333
SickleAlternateAttackStyle.InfernalRush => 1.48f,
334
SickleAlternateAttackStyle.VoidStep => 1.65f,
335
_ => 1.25f
336
};
337
Projectile.damage = Math.Max(1, (int)(Projectile.originalDamage * multiplier));
212
338
}
213
339
340
switch (sickle.AlternateAttackStyle)
341
{
342
case SickleAlternateAttackStyle.BoneRush:
343
{
344
float lunge = (float)Math.Sin(progress * MathHelper.Pi);
345
weaponAngle = aimRotation + (float)Math.Sin(progress * MathHelper.TwoPi) * 0.2f;
346
weaponFlipped = player.direction < 0;
347
gripPosition += aimRotation.ToRotationVector2() * lunge * 18f;
348
actionSegment = progress < 0.5f ? 0 : 1;
349
ApplyDash(player, 5.5f, 0.14f);
350
break;
351
}
352
case SickleAlternateAttackStyle.InfernalRush:
353
SetArc(aimRotation, -2.8f, 4.1f, SmoothStep(progress), player.direction, 0);
354
gripPosition += aimRotation.ToRotationVector2() * (float)Math.Sin(progress * MathHelper.Pi) * 12f;
355
ApplyDash(player, 8f, 0.1f);
356
break;
357
case SickleAlternateAttackStyle.VoidStep:
358
SetArc(aimRotation, -3.2f, 6.15f, SmoothStep(progress), player.direction, progress < 0.48f ? 0 : 1);
359
gripPosition += aimRotation.ToRotationVector2() * (float)Math.Sin(progress * MathHelper.Pi) * 16f;
360
ApplyDash(player, 10f, 0.08f);
361
break;
362
default:
363
SetArc(aimRotation, -2.5f, 1.5f, EaseOutCubic(progress), player.direction, 0);
364
gripPosition += aimRotation.ToRotationVector2() * (float)Math.Sin(progress * MathHelper.Pi) * 10f;
365
ApplyDash(player, 6.5f, 0.12f);
366
break;
367
}
368
369
canDamage = progress is > 0.08f and < 0.94f;
370
TrySpawnAttack(player, sickle, progress, 0.42f, true);
214
371
if (progress >= 1f)
215
372
Projectile.Kill();
216
373
}
217
374
375
private void UpdateCrossCut(float progress, float centerAngle, int direction, bool charged)
376
{
377
float split = charged ? 0.48f : 0.54f;
378
if (progress < split)
379
{
380
float segmentProgress = SmoothStep(progress / split);
381
SetArc(centerAngle, charged ? -2.72f : -2.25f, charged ? 1.28f : 0.82f, segmentProgress, direction, 0);
382
}
383
else
384
{
385
float segmentProgress = SmoothStep((progress - split) / (1f - split));
386
SetArc(centerAngle, charged ? 2.58f : 2.18f, charged ? -1.5f : -0.92f, segmentProgress, direction, 1);
387
}
388
}
389
390
private void SetArc(float centerAngle, float startOffset, float endOffset, float progress, int direction, int segment)
391
{
392
float start = centerAngle + startOffset * direction;
393
float end = centerAngle + endOffset * direction;
394
weaponAngle = MathHelper.Lerp(start, end, progress);
395
weaponFlipped = end < start;
396
actionSegment = segment;
397
}
398
399
private void ApplyDash(Player player, float speed, float verticalDamping)
400
{
401
if (dashApplied || Projectile.owner != Main.myPlayer)
402
return;
403
404
dashApplied = true;
405
Vector2 direction = aimRotation.ToRotationVector2();
406
player.velocity = new Vector2(direction.X * speed, player.velocity.Y * verticalDamping + direction.Y * speed * 0.45f);
407
}
408
409
private void TrySpawnAttack(Player player, NormalSickle sickle, float progress, float releasePoint, bool empowered)
410
{
411
if (spawnedAttack || progress < releasePoint)
412
return;
413
414
spawnedAttack = true;
415
if (Projectile.owner == Main.myPlayer)
416
sickle.SpawnAttackProjectiles(player, Projectile, chargeRatio, empowered);
417
}
418
218
419
private void UpdatePlayerArm(Player player)
219
420
{
220
421
float armRotation = weaponAngle - MathHelper.PiOver2;
@@ -224,21 +425,98 @@ public class SickleSwingProjectile : ModProjectile
224
425
225
426
private void SpawnMotionEffects(Player player, NormalSickle sickle)
226
427
{
227
Vector2 bladeTip = player.MountedCenter + weaponAngle.ToRotationVector2() * sickle.SickleReach;
228
float intensity = IsCharging ? 0.3f + chargeRatio : 0.65f + sickle.SickleTier * 0.2f;
428
Vector2 bladeTip = gripPosition + weaponAngle.ToRotationVector2() * sickle.SickleReach;
429
bool charging = IsAlternateAttack && sickle.SupportsCharge && !IsReleasedCharge;
430
float intensity = charging ? 0.3f + chargeRatio : 0.65f + sickle.SickleTier * 0.2f;
229
431
Lighting.AddLight(bladeTip, sickle.SickleColor.ToVector3() * intensity * 0.45f);
230
432
231
433
int chance = Math.Max(1, 5 - sickle.SickleTier);
232
434
if (!Main.rand.NextBool(chance))
233
435
return;
234
436
235
Vector2 velocity = IsCharging
437
Vector2 velocity = charging
236
438
? (bladeTip - player.MountedCenter).SafeNormalize(Vector2.UnitY).RotatedBy(MathHelper.PiOver2) * Main.rand.NextFloat(-1.2f, 1.2f)
237
: weaponAngle.ToRotationVector2().RotatedBy(MathHelper.PiOver2 * SwingDirection) * Main.rand.NextFloat(0.6f, 2.4f);
439
: weaponAngle.ToRotationVector2().RotatedBy(weaponFlipped ? -MathHelper.PiOver2 : MathHelper.PiOver2) * Main.rand.NextFloat(0.6f, 2.4f);
238
440
Dust dust = Dust.NewDustPerfect(bladeTip, sickle.SickleDustType, velocity, 90, sickle.SickleColor, 0.75f + sickle.SickleTier * 0.12f);
239
441
dust.noGravity = true;
240
442
}
241
443
444
private void DrawSlashTrail(NormalSickle sickle)
445
{
446
Texture2D pixel = TextureAssets.MagicPixel.Value;
447
int segmentCount = Math.Min(HistoryLength - 1, 7 + sickle.SickleTier * 2);
448
float empoweredScale = IsAlternateAttack ? 1.25f : 1f;
449
450
for (int index = segmentCount - 1; index >= 0; index--)
451
{
452
if (gripHistory[index] == Vector2.Zero || gripHistory[index + 1] == Vector2.Zero)
453
continue;
454
if (sickle.SwingStyle is SickleSwingStyle.Overhead or SickleSwingStyle.CrossCut && index % 3 == 2)
455
continue;
456
457
Vector2 currentTip = gripHistory[index] + angleHistory[index].ToRotationVector2() * sickle.SickleReach;
458
Vector2 oldTip = gripHistory[index + 1] + angleHistory[index + 1].ToRotationVector2() * sickle.SickleReach;
459
Vector2 difference = oldTip - currentTip;
460
float length = difference.Length();
461
if (length < 0.5f || length > sickle.SickleReach * 1.2f)
462
continue;
463
464
float strength = 1f - index / (float)segmentCount;
465
float width = sickle.TrailWidth * strength * empoweredScale;
466
if (sickle.SwingStyle == SickleSwingStyle.Rising)
467
width *= 0.8f + strength * 0.65f;
468
if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
469
width *= 1f + (float)Math.Sin(index * 1.7f + Main.GlobalTimeWrappedHourly * 8f) * 0.18f;
470
471
DrawTrailSegment(pixel, currentTip, difference, sickle.SickleColor with { A = 0 }, width + 4f, strength * 0.34f);
472
DrawTrailSegment(pixel, currentTip, difference, sickle.SickleSecondaryColor with { A = 0 }, width * 0.42f, strength * 0.62f);
473
474
if (sickle.SwingStyle == SickleSwingStyle.Circular)
475
{
476
Vector2 innerCurrent = gripHistory[index] + angleHistory[index].ToRotationVector2() * (sickle.SickleReach * 0.82f);
477
Vector2 innerOld = gripHistory[index + 1] + angleHistory[index + 1].ToRotationVector2() * (sickle.SickleReach * 0.82f);
478
DrawTrailSegment(pixel, innerCurrent, innerOld - innerCurrent, sickle.SickleSecondaryColor with { A = 0 }, width * 0.32f, strength * 0.5f);
479
}
480
else if (sickle.SwingStyle == SickleSwingStyle.SoulOrbit)
481
{
482
float wave = (float)Math.Sin(index * 1.4f + Main.GlobalTimeWrappedHourly * 7f) * 7f;
483
Vector2 normal = difference.SafeNormalize(Vector2.UnitX).RotatedBy(MathHelper.PiOver2) * wave;
484
DrawTrailSegment(pixel, currentTip + normal, difference, sickle.SickleSecondaryColor with { A = 0 }, width * 0.28f, strength * 0.52f);
485
}
486
else if (sickle.SwingStyle == SickleSwingStyle.VoidOrbit)
487
{
488
Vector2 midpoint = currentTip + difference * 0.5f;
489
float moteSize = 2.5f + strength * 4f;
490
Main.EntitySpriteDraw(pixel, midpoint - Main.screenPosition, null, sickle.SickleSecondaryColor with { A = 0 },
491
MathHelper.PiOver4, new Vector2(0.5f), new Vector2(moteSize), SpriteEffects.None);
492
}
493
}
494
}
495
496
private static void DrawTrailSegment(Texture2D pixel, Vector2 start, Vector2 difference, Color color, float width, float opacity)
497
{
498
Main.EntitySpriteDraw(pixel, start - Main.screenPosition, null, color * opacity,
499
difference.ToRotation(), new Vector2(0f, 0.5f),
500
new Vector2(difference.Length(), Math.Max(1f, width)), SpriteEffects.None);
501
}
502
503
private static void DrawWeapon(Texture2D texture, NormalSickle sickle, Vector2 position, float angle, bool flipped, Color color, float opacity, float scale)
504
{
505
Vector2 anchor = sickle.HandleAnchor;
506
SpriteEffects effects = SpriteEffects.None;
507
float rotationCorrection = sickle.TextureRotationCorrection;
508
if (flipped)
509
{
510
anchor.Y = 1f - anchor.Y;
511
rotationCorrection = -rotationCorrection;
512
effects = SpriteEffects.FlipVertically;
513
}
514
515
Vector2 origin = new(texture.Width * anchor.X, texture.Height * anchor.Y);
516
Main.EntitySpriteDraw(texture, position - Main.screenPosition, null, color * opacity,
517
angle + rotationCorrection, origin, scale, effects);
518
}
519
242
520
private void DrawChargeRing(Player player, NormalSickle sickle)
243
521
{
244
522
Texture2D pixel = TextureAssets.MagicPixel.Value;
@@ -248,17 +526,38 @@ public class SickleSwingProjectile : ModProjectile
248
526
{
249
527
float angle = Main.GlobalTimeWrappedHourly * (2f + sickle.SickleTier * 0.25f) + index * MathHelper.TwoPi / motes;
250
528
Vector2 position = player.MountedCenter + angle.ToRotationVector2() * radius - Main.screenPosition;
251
float moteScale = 2f + chargeRatio * 3f + (index % 2);
252
Color color = sickle.SickleColor with { A = 0 };
253
Main.EntitySpriteDraw(pixel, position, null, color * (0.55f + chargeRatio * 0.4f), angle, new Vector2(0.5f), new Vector2(moteScale, 1.5f), SpriteEffects.None);
529
float moteScale = 2f + chargeRatio * 3f + index % 2;
530
Color color = Color.Lerp(sickle.SickleColor, sickle.SickleSecondaryColor, index % 2) with { A = 0 };
531
Main.EntitySpriteDraw(pixel, position, null, color * (0.55f + chargeRatio * 0.4f), angle + MathHelper.PiOver4,
532
new Vector2(0.5f), new Vector2(moteScale), SpriteEffects.None);
254
533
}
255
534
}
256
535
257
private void PushAngleHistory()
536
private void UpdateHistory()
258
537
{
259
for (int index = angleHistory.Length - 1; index > 0; index--)
538
if (historySegment != actionSegment)
539
{
540
historySegment = actionSegment;
541
ResetHistory();
542
return;
543
}
544
545
for (int index = HistoryLength - 1; index > 0; index--)
546
{
260
547
angleHistory[index] = angleHistory[index - 1];
548
gripHistory[index] = gripHistory[index - 1];
549
flipHistory[index] = flipHistory[index - 1];
550
}
261
551
angleHistory[0] = weaponAngle;
552
gripHistory[0] = gripPosition;
553
flipHistory[0] = weaponFlipped;
554
}
555
556
private void ResetHistory()
557
{
558
Array.Fill(angleHistory, weaponAngle);
559
Array.Fill(gripHistory, gripPosition);
560
Array.Fill(flipHistory, weaponFlipped);
262
561
}
263
562
264
563
private bool TryGetSickle(out Player player, out NormalSickle sickle)
@@ -275,4 +574,8 @@ public class SickleSwingProjectile : ModProjectile
275
574
}
276
575
277
576
private static float SmoothStep(float value) => value * value * (3f - 2f * value);
577
578
private static float EaseOutCubic(float value) => 1f - (float)Math.Pow(1f - value, 3f);
579
580
private static float EaseOutSine(float value) => (float)Math.Sin(value * MathHelper.PiOver2);
278
581
}