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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

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

XFEstudio/DeathMod

死神终极碎裂中心视觉特效同步与检测支持

为死神终极碎裂技能增加中心视觉特效的同步与检测功能。包括:ReaperDeathShatterProjectile 新增 visualCenter 和 drawCenterVisual 字段及其序列化、绘制和网络同步;Spawn 方法及调用处参数扩展;自动化测试用例完善,覆盖视觉中心同步与断言;仅在 drawCenterVisual 为 true 时绘制中心视觉,位置由 visualCenter 控制。提升多人联机下视觉一致性和测试精度。

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

代码差异

3 个文件 +81 -11
Modified Common/ReaperDedicatedServerSelfTest.cs +49 -4
@@ -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 }
Modified Projectiles/ReaperActionControllerProjectile.cs +6 -2
@@ -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
Modified Projectiles/ReaperDeathShatterProjectile.cs +26 -5
@@ -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 }