返回提交历史
Modified
Common/ReaperDedicatedServerSelfTest.cs
+49
-4
Modified
Projectiles/ReaperActionControllerProjectile.cs
+6
-2
Modified
Projectiles/ReaperDeathShatterProjectile.cs
+26
-5
XFEstudio/DeathMod
死神终极碎裂中心视觉特效同步与检测支持
为死神终极碎裂技能增加中心视觉特效的同步与检测功能。包括:ReaperDeathShatterProjectile 新增 visualCenter 和 drawCenterVisual 字段及其序列化、绘制和网络同步;Spawn 方法及调用处参数扩展;自动化测试用例完善,覆盖视觉中心同步与断言;仅在 drawCenterVisual 为 true 时绘制中心视觉,位置由 visualCenter 控制。提升多人联机下视觉一致性和测试精度。
44d3152
代码差异
3 个文件
+81
-11
@@ -214,6 +214,24 @@ public sealed class ReaperDedicatedServerSelfTest : ModCommand
214
214
player, Math.Max(1, player.GetWeaponDamage(player.HeldItem)),
215
215
player.GetWeaponKnockback(player.HeldItem,
216
216
player.HeldItem.knockBack));
217
Vector2 shatterVisualCenter = player.Center
218
+ new Vector2(73f, -41f);
219
int shatterIndex = ReaperDeathShatterProjectile.Spawn(
220
player.GetSource_Misc("SoulHarvest:ServerSelfTestShatterCenter"),
221
playerIndex, snapshot, trainingDummy, 1, 731,
222
shatterVisualCenter, drawCenterVisual: true);
223
Require(shatterIndex >= 0 && shatterIndex < Main.maxProjectiles,
224
"server did not create the Death shatter center test projectile");
225
Require(Main.projectile[shatterIndex].ModProjectile
226
is ReaperDeathShatterProjectile shatter
227
&& shatter.DrawsCenterVisual
228
&& Vector2.DistanceSquared(shatter.VisualCenter,
229
shatterVisualCenter) < 0.01f,
230
"Death shatter did not preserve the shared cut center");
231
Require(Vector2.DistanceSquared(Main.projectile[shatterIndex].Center,
232
trainingDummy.Center) < 0.01f,
233
"Death shatter visual center replaced its damage target center");
234
Main.projectile[shatterIndex].Kill();
217
235
Vector2 grip = player.MountedCenter;
218
236
Vector2[] path = SickleSwingProjectile.BuildDeathDomainRiftPath(
219
237
grip, 0.37f, -1, snapshot, 1f);
@@ -520,6 +538,9 @@ public sealed class ReaperMultiplayerClientSelfTest : ModSystem
520
538
private bool sawWingTrailInitialTiming;
521
539
private bool sawWingTrailAdvancedTiming;
522
540
private bool sawDeathUltimateShatter;
541
private bool deathUltimateCenterAligned;
542
private Vector2 deathUltimateCutCenter;
543
private bool hasDeathUltimateCutCenter;
523
544
private readonly HashSet<int> deathUltimateVisualPhases = [];
524
545
525
546
public override void OnWorldLoad()
@@ -544,6 +565,9 @@ public sealed class ReaperMultiplayerClientSelfTest : ModSystem
544
565
sawWingTrailInitialTiming = false;
545
566
sawWingTrailAdvancedTiming = false;
546
567
sawDeathUltimateShatter = false;
568
deathUltimateCenterAligned = false;
569
deathUltimateCutCenter = Vector2.Zero;
570
hasDeathUltimateCutCenter = false;
547
571
deathUltimateVisualPhases.Clear();
548
572
}
549
573
@@ -902,7 +926,19 @@ public sealed class ReaperMultiplayerClientSelfTest : ModSystem
902
926
{
903
927
if (projectile.owner != player.whoAmI)
904
928
continue;
905
sawDeathUltimateShatter |= projectile.type == shatterType;
929
if (projectile.type == shatterType
930
&& projectile.ModProjectile
931
is ReaperDeathShatterProjectile shatter)
932
{
933
sawDeathUltimateShatter = true;
934
if (shatter.DrawsCenterVisual
935
&& hasDeathUltimateCutCenter
936
&& Vector2.DistanceSquared(shatter.VisualCenter,
937
deathUltimateCutCenter) <= 4f)
938
{
939
deathUltimateCenterAligned = true;
940
}
941
}
906
942
if (projectile.ModProjectile is not ReaperStrikeProjectile)
907
943
continue;
908
944
MyGlobalProjectile data = projectile.GetGlobalProjectile<
@@ -913,19 +949,28 @@ public sealed class ReaperMultiplayerClientSelfTest : ModSystem
913
949
&& data.ReaperPhase < ReaperDeathUltimateGeometry.CutCount)
914
950
{
915
951
deathUltimateVisualPhases.Add(data.ReaperPhase);
952
Vector2 center = projectile.Center
953
+ projectile.velocity.SafeNormalize(Vector2.UnitX)
954
* (ReaperDeathUltimateGeometry.CutLength * 0.5f);
955
if (!hasDeathUltimateCutCenter)
956
{
957
deathUltimateCutCenter = center;
958
hasDeathUltimateCutCenter = true;
959
}
916
960
}
917
961
}
918
962
919
963
if (deathUltimateVisualPhases.Count
920
964
== ReaperDeathUltimateGeometry.CutCount
921
&& sawDeathUltimateShatter)
965
&& sawDeathUltimateShatter
966
&& deathUltimateCenterAligned)
922
967
{
923
Finish($"PASS live-handshake=1/1 live-right-click-controller=1/1 live-right-click-echo=1/1 live-right-click-strike=1/1 live-primary-dummy-hit=1/1 live-energy-forms=7/7 live-form-projectiles={liveProjectileFormsPassed}/6 live-domain-slash=1/1 live-domain-telegraph-observed={sawDomainTelegraph} live-max-tempo-rift=65/full live-wing-timing=300/300 live-death-ultimate-cuts={deathUltimateVisualPhases.Count}/{ReaperDeathUltimateGeometry.CutCount} live-death-ultimate-shatter=1/1 form=Death");
968
Finish($"PASS live-handshake=1/1 live-right-click-controller=1/1 live-right-click-echo=1/1 live-right-click-strike=1/1 live-primary-dummy-hit=1/1 live-energy-forms=7/7 live-form-projectiles={liveProjectileFormsPassed}/6 live-domain-slash=1/1 live-domain-telegraph-observed={sawDomainTelegraph} live-max-tempo-rift=65/full live-wing-timing=300/300 live-death-ultimate-cuts={deathUltimateVisualPhases.Count}/{ReaperDeathUltimateGeometry.CutCount} live-death-ultimate-shatter=1/1 live-death-shatter-center=1/1 form=Death");
924
969
return;
925
970
}
926
971
if (timer >= 900)
927
972
{
928
Finish($"FAIL live-death-ultimate-timeout cuts={deathUltimateVisualPhases.Count}/{ReaperDeathUltimateGeometry.CutCount} shatter={sawDeathUltimateShatter}");
973
Finish($"FAIL live-death-ultimate-timeout cuts={deathUltimateVisualPhases.Count}/{ReaperDeathUltimateGeometry.CutCount} shatter={sawDeathUltimateShatter} centerAligned={deathUltimateCenterAligned}");
929
974
}
930
975
return;
931
976
}
@@ -965,6 +965,7 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
965
965
private void ExecuteDeathUltimateShatter(Player player)
966
966
{
967
967
HashSet<int> processedRoots = [];
968
bool centerVisualSpawned = false;
968
969
foreach (NPC npc in Main.ActiveNPCs)
969
970
{
970
971
int root = npc.realLife >= 0 ? npc.realLife : npc.whoAmI;
@@ -978,8 +979,11 @@ public sealed class ReaperActionControllerProjectile : ModProjectile
978
979
player.whoAmI, Projectile.identity);
979
980
if (storedDamage <= 0)
980
981
continue;
981
ReaperDeathShatterProjectile.Spawn(Projectile.GetSource_FromThis(),
982
player.whoAmI, snapshot, target, storedDamage, Projectile.identity);
982
int shatterIndex = ReaperDeathShatterProjectile.Spawn(
983
Projectile.GetSource_FromThis(), player.whoAmI, snapshot,
984
target, storedDamage, Projectile.identity, ultimateFocusWorld,
985
drawCenterVisual: !centerVisualSpawned);
986
centerVisualSpawned |= shatterIndex >= 0;
983
987
}
984
988
}
985
989
@@ -16,13 +16,19 @@ public sealed class ReaperDeathShatterProjectile : ModProjectile
16
16
private int actionId;
17
17
private int age;
18
18
private bool configured;
19
private Vector2 visualCenter;
20
private bool drawCenterVisual;
19
21
private bool serverAuthorized;
20
22
private bool serverDamageTriggered;
21
23
24
internal Vector2 VisualCenter => visualCenter;
25
internal bool DrawsCenterVisual => drawCenterVisual;
26
22
27
public override string Texture => "Terraria/Images/Projectile_0";
23
28
24
29
internal static int Spawn(Terraria.DataStructures.IEntitySource source, int owner,
25
in SickleCombatSnapshot snapshot, NPC target, int accumulatedDamage, int actionId)
30
in SickleCombatSnapshot snapshot, NPC target, int accumulatedDamage,
31
int actionId, Vector2 visualCenter, bool drawCenterVisual)
26
32
{
27
33
if (Main.netMode == NetmodeID.MultiplayerClient || accumulatedDamage <= 0)
28
34
return -1;
@@ -36,7 +42,8 @@ public sealed class ReaperDeathShatterProjectile : ModProjectile
36
42
projectile.originalDamage = damage;
37
43
projectile.ArmorPenetration = Math.Max(0, target.defense);
38
44
if (projectile.ModProjectile is ReaperDeathShatterProjectile shatter)
39
shatter.Configure(snapshot, target.whoAmI, actionId);
45
shatter.Configure(snapshot, target.whoAmI, actionId,
46
visualCenter, drawCenterVisual);
40
47
projectile.GetGlobalProjectile<MyGlobalProjectile>().ConfigureReaperProjectile(
41
48
projectile, snapshot, ReaperHitKind.UltimateDerived, 63, 0f, actionId);
42
49
ReaperProjectileHelper.SyncNewProjectile(projectile);
@@ -93,14 +100,14 @@ public sealed class ReaperDeathShatterProjectile : ModProjectile
93
100
94
101
public override bool PreDraw(ref Color lightColor)
95
102
{
96
if (!configured || Main.dedServ)
103
if (!configured || !drawCenterVisual || Main.dedServ)
97
104
return false;
98
105
float opacity = 1f - MathHelper.Clamp(age / 11f, 0f, 1f);
99
106
for (int shard = 0; shard < 12; shard++)
100
107
{
101
108
float angle = shard * MathHelper.TwoPi / 12f + actionId * 0.013f;
102
109
Vector2 direction = angle.ToRotationVector2();
103
Vector2 start = Projectile.Center + direction * (8f + age * 1.2f);
110
Vector2 start = visualCenter + direction * (8f + age * 1.2f);
104
111
Vector2 end = start + direction * (30f + age * 5f);
105
112
DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, end,
106
113
new Color(225, 22, 62, 0) * opacity, 4.5f * opacity + 0.8f);
@@ -118,6 +125,8 @@ public sealed class ReaperDeathShatterProjectile : ModProjectile
118
125
snapshot.Write(writer);
119
126
writer.Write((short)targetIndex);
120
127
writer.Write(actionId);
128
writer.WriteVector2(visualCenter);
129
writer.Write(drawCenterVisual);
121
130
writer.Write((byte)Math.Clamp(age, 0, byte.MaxValue));
122
131
}
123
132
@@ -133,24 +142,36 @@ public sealed class ReaperDeathShatterProjectile : ModProjectile
133
142
SickleCombatSnapshot incomingSnapshot = SickleCombatSnapshot.Read(reader);
134
143
int incomingTarget = reader.ReadInt16();
135
144
int incomingAction = reader.ReadInt32();
145
Vector2 incomingVisualCenter = reader.ReadVector2();
146
bool incomingDrawCenterVisual = reader.ReadBoolean();
136
147
int incomingAge = reader.ReadByte();
137
148
if (Main.netMode == NetmodeID.Server)
138
149
return;
139
150
snapshot = incomingSnapshot;
140
151
targetIndex = incomingTarget;
141
152
actionId = incomingAction;
153
visualCenter = IsFinite(incomingVisualCenter)
154
? incomingVisualCenter
155
: Projectile.Center;
156
drawCenterVisual = incomingDrawCenterVisual;
142
157
age = incomingAge;
143
158
configured = true;
144
159
}
145
160
146
private void Configure(in SickleCombatSnapshot value, int target, int parentActionId)
161
private void Configure(in SickleCombatSnapshot value, int target,
162
int parentActionId, Vector2 center, bool rendersCenterVisual)
147
163
{
148
164
snapshot = value;
149
165
targetIndex = target;
150
166
actionId = parentActionId;
167
visualCenter = IsFinite(center) ? center : Projectile.Center;
168
drawCenterVisual = rendersCenterVisual;
151
169
age = 0;
152
170
configured = true;
153
171
serverAuthorized = Main.netMode != NetmodeID.MultiplayerClient;
154
172
serverDamageTriggered = false;
155
173
}
174
175
private static bool IsFinite(Vector2 value)
176
=> float.IsFinite(value.X) && float.IsFinite(value.Y);
156
177
}