返回提交历史
Modified
Common/DeathDomainTrailVisualSystem.cs
+195
-142
Modified
Projectiles/ReaperDeathDomainRiftProjectile.cs
+36
-14
XFEstudio/DeathMod
优化死神领域裂缝批处理性能
73680c9
代码差异
2 个文件
+231
-156
@@ -19,10 +19,10 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
19
19
private static readonly Dictionary<long, TrailDrawState> drawStates = [];
20
20
private static readonly VertexPositionColorTexture[] vertices =
21
21
new VertexPositionColorTexture[MaximumPoints * 2];
22
private static readonly VertexPositionColorTexture[] outlineVertices =
23
new VertexPositionColorTexture[(MaximumPoints - 1) * 2 * 6];
24
private static readonly bool[] outlineVisible =
25
new bool[(MaximumPoints - 1) * 2];
22
private static VertexPositionColorTexture[] mergedOutlineVertices =
23
new VertexPositionColorTexture[4096];
24
private static VertexPositionColorTexture[] mergedBackdropVertices =
25
new VertexPositionColorTexture[4096];
26
26
private static readonly short[] indices = CreateIndices();
27
27
private static BasicEffect? effect;
28
28
@@ -44,8 +44,23 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
44
44
return;
45
45
}
46
46
long key = ((long)owner << 32) | (uint)identity;
47
if (drawStates.TryGetValue(key, out TrailDrawState existing)
48
&& existing.MergeOverlappingRims == mergeOverlappingRims
49
&& ReferenceEquals(existing.Points, points))
50
{
51
// Persistent rifts submit the same immutable path every update. Only
52
// opacity and freshness change, so keep their cached world bounds.
53
drawStates[key] = existing with
54
{
55
Opacity = opacity,
56
UpdateTick = Main.GameUpdateCount
57
};
58
return;
59
}
60
47
61
drawStates[key] = new TrailDrawState(points, width, opacity,
48
mergeOverlappingRims, CalculateBounds(points, width),
62
mergeOverlappingRims,
63
CalculateTrailBounds(points, width * 1.72f),
49
64
Main.GameUpdateCount);
50
65
}
51
66
@@ -67,17 +82,22 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
67
82
drawStates.Remove(key);
68
83
}
69
84
70
public override void OnWorldUnload() => drawStates.Clear();
85
public override void OnWorldUnload() => ClearVisualState();
71
86
72
87
public override void Unload()
73
88
{
74
drawStates.Clear();
89
ClearVisualState();
75
90
BasicEffect? oldEffect = effect;
76
91
effect = null;
77
92
if (oldEffect is not null && !Main.dedServ)
78
93
Main.QueueMainThreadAction(oldEffect.Dispose);
79
94
}
80
95
96
private static void ClearVisualState()
97
{
98
drawStates.Clear();
99
}
100
81
101
public override void PostDrawTiles()
82
102
{
83
103
if (Main.gameMenu || drawStates.Count == 0
@@ -101,38 +121,26 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
101
121
graphicsDevice.DepthStencilState = DepthStencilState.None;
102
122
graphicsDevice.RasterizerState = RasterizerState.CullNone;
103
123
graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
124
DrawMergedTrails(graphicsDevice);
104
125
105
foreach ((long visualId, TrailDrawState state) in drawStates)
126
foreach (TrailDrawState state in drawStates.Values)
106
127
{
107
128
if (Main.GameUpdateCount > state.UpdateTick + 1)
108
129
continue;
130
if (state.MergeOverlappingRims)
131
continue;
132
if (!IsOnScreen(state.Bounds))
133
continue;
109
134
int pointCount = Math.Min(MaximumPoints, state.Points.Count);
110
135
if (pointCount < 2)
111
136
continue;
112
if (state.MergeOverlappingRims)
113
{
114
BuildMergedOutlineVisibility(visualId, state, pointCount);
115
DrawOutlineBand(graphicsDevice, state, pointCount,
116
state.Width * 0.70f, state.Width * 0.86f,
117
new Color(174, 0, 44, 0) * (state.Opacity * 0.24f));
118
DrawOutlineBand(graphicsDevice, state, pointCount,
119
state.Width * 0.585f, state.Width * 0.70f,
120
new Color(244, 12, 66, 235) * state.Opacity);
121
DrawOutlineBand(graphicsDevice, state, pointCount,
122
state.Width * 0.50f, state.Width * 0.585f,
123
new Color(255, 187, 178, 235)
124
* (state.Opacity * 0.88f));
125
}
126
else
127
{
128
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.72f,
129
new Color(174, 0, 44, 0) * (state.Opacity * 0.24f));
130
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.40f,
131
new Color(244, 12, 66, 235) * state.Opacity);
132
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.17f,
133
new Color(255, 187, 178, 235)
134
* (state.Opacity * 0.88f));
135
}
137
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.72f,
138
new Color(174, 0, 44, 0) * (state.Opacity * 0.24f));
139
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.40f,
140
new Color(244, 12, 66, 235) * state.Opacity);
141
DrawSolidRibbon(graphicsDevice, state, state.Width * 1.17f,
142
new Color(255, 187, 178, 235)
143
* (state.Opacity * 0.88f));
136
144
BuildVertices(state, state.Width, Color.White * state.Opacity);
137
145
effect.TextureEnabled = true;
138
146
for (int layer = 0; layer < 3; layer++)
@@ -149,71 +157,96 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
149
157
}
150
158
}
151
159
152
private static void BuildMergedOutlineVisibility(long visualId,
153
TrailDrawState state, int pointCount)
160
private static void DrawMergedTrails(GraphicsDevice graphicsDevice)
154
161
{
155
Array.Clear(outlineVisible, 0, outlineVisible.Length);
156
for (int segment = 0; segment < pointCount - 1; segment++)
162
if (effect is null)
163
return;
164
165
// Render every rim first and the opaque, world-aligned domain ribbons
166
// afterwards. The common backdrop naturally covers rims inside an
167
// overlap, producing one continuous surface without any pairwise trail
168
// intersection tests. Work therefore stays linear as rifts accumulate.
169
DrawMergedOutlineBatch(graphicsDevice, 0.70f, 0.86f,
170
new Color(174, 0, 44, 0), 0.24f);
171
DrawMergedOutlineBatch(graphicsDevice, 0.585f, 0.70f,
172
new Color(244, 12, 66, 235), 1f);
173
DrawMergedOutlineBatch(graphicsDevice, 0.50f, 0.585f,
174
new Color(255, 187, 178, 235), 0.88f);
175
176
int backdropVertexCount = BuildMergedBackdropBatch();
177
if (backdropVertexCount < 3)
178
return;
179
effect.TextureEnabled = true;
180
for (int layer = 0; layer < 3; layer++)
157
181
{
158
Vector2 start = state.Points[segment];
159
Vector2 end = state.Points[segment + 1];
160
Vector2 tangent = (end - start).SafeNormalize(Vector2.UnitX);
161
Vector2 normal = tangent.RotatedBy(MathHelper.PiOver2);
162
Vector2 middle = (start + end) * 0.5f;
163
float middleProgress = (segment + 0.5f)
164
/ Math.Max(1f, pointCount - 1f);
165
float boundaryHalfWidth = state.Width
166
* GetTrailTaper(middleProgress) * 0.5f;
167
for (int sideIndex = 0; sideIndex < 2; sideIndex++)
182
effect.Texture = DeathDomainBackdropTextureSystem.GetLayer(layer);
183
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
168
184
{
169
float side = sideIndex == 0 ? -1f : 1f;
170
Vector2 boundaryProbe = middle
171
+ normal * side * boundaryHalfWidth;
172
outlineVisible[segment * 2 + sideIndex] =
173
!IsCoveredByAnotherMergedTrail(boundaryProbe, visualId);
185
pass.Apply();
186
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList,
187
mergedBackdropVertices, 0, backdropVertexCount / 3);
174
188
}
175
189
}
176
190
}
177
191
178
private static void DrawOutlineBand(GraphicsDevice graphicsDevice,
179
TrailDrawState state, int pointCount, float innerHalfWidth,
180
float outerHalfWidth, Color color)
192
private static void DrawMergedOutlineBatch(GraphicsDevice graphicsDevice,
193
float innerWidthRatio, float outerWidthRatio, Color baseColor,
194
float opacityMultiplier)
181
195
{
182
196
if (effect is null)
183
197
return;
184
198
185
199
int vertexIndex = 0;
186
for (int segment = 0; segment < pointCount - 1; segment++)
200
foreach (TrailDrawState state in drawStates.Values)
187
201
{
188
Vector2 start = state.Points[segment];
189
Vector2 end = state.Points[segment + 1];
190
Vector2 startNormal = GetTrailNormal(state.Points, segment,
191
pointCount);
192
Vector2 endNormal = GetTrailNormal(state.Points, segment + 1,
193
pointCount);
194
float startTaper = GetTrailTaper(segment
195
/ Math.Max(1f, pointCount - 1f));
196
float endTaper = GetTrailTaper((segment + 1f)
197
/ Math.Max(1f, pointCount - 1f));
198
for (int sideIndex = 0; sideIndex < 2; sideIndex++)
202
if (!state.MergeOverlappingRims
203
|| Main.GameUpdateCount > state.UpdateTick + 1
204
|| !IsOnScreen(state.Bounds))
199
205
{
200
if (!outlineVisible[segment * 2 + sideIndex])
201
continue;
202
float side = sideIndex == 0 ? -1f : 1f;
203
Vector2 innerStart = start
204
+ startNormal * side * innerHalfWidth * startTaper;
205
Vector2 outerStart = start
206
+ startNormal * side * outerHalfWidth * startTaper;
207
Vector2 innerEnd = end
208
+ endNormal * side * innerHalfWidth * endTaper;
209
Vector2 outerEnd = end
210
+ endNormal * side * outerHalfWidth * endTaper;
211
WriteOutlineVertex(ref vertexIndex, outerStart, color);
212
WriteOutlineVertex(ref vertexIndex, innerStart, color);
213
WriteOutlineVertex(ref vertexIndex, outerEnd, color);
214
WriteOutlineVertex(ref vertexIndex, outerEnd, color);
215
WriteOutlineVertex(ref vertexIndex, innerStart, color);
216
WriteOutlineVertex(ref vertexIndex, innerEnd, color);
206
continue;
207
}
208
int pointCount = Math.Min(MaximumPoints, state.Points.Count);
209
float innerHalfWidth = state.Width * innerWidthRatio;
210
float outerHalfWidth = state.Width * outerWidthRatio;
211
Color color = baseColor * (state.Opacity * opacityMultiplier);
212
for (int segment = 0; segment < pointCount - 1; segment++)
213
{
214
Vector2 start = state.Points[segment];
215
Vector2 end = state.Points[segment + 1];
216
Vector2 startNormal = GetTrailNormal(state.Points, segment,
217
pointCount);
218
Vector2 endNormal = GetTrailNormal(state.Points, segment + 1,
219
pointCount);
220
float startTaper = GetTrailTaper(segment
221
/ Math.Max(1f, pointCount - 1f));
222
float endTaper = GetTrailTaper((segment + 1f)
223
/ Math.Max(1f, pointCount - 1f));
224
for (int sideIndex = 0; sideIndex < 2; sideIndex++)
225
{
226
float side = sideIndex == 0 ? -1f : 1f;
227
Vector2 innerStart = start
228
+ startNormal * side * innerHalfWidth * startTaper;
229
Vector2 outerStart = start
230
+ startNormal * side * outerHalfWidth * startTaper;
231
Vector2 innerEnd = end
232
+ endNormal * side * innerHalfWidth * endTaper;
233
Vector2 outerEnd = end
234
+ endNormal * side * outerHalfWidth * endTaper;
235
EnsureBatchCapacity(ref mergedOutlineVertices,
236
vertexIndex + 6);
237
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
238
outerStart, color, Vector2.Zero);
239
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
240
innerStart, color, Vector2.Zero);
241
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
242
outerEnd, color, Vector2.Zero);
243
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
244
outerEnd, color, Vector2.Zero);
245
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
246
innerStart, color, Vector2.Zero);
247
WriteBatchVertex(mergedOutlineVertices, ref vertexIndex,
248
innerEnd, color, Vector2.Zero);
249
}
217
250
}
218
251
}
219
252
if (vertexIndex < 3)
@@ -224,74 +257,87 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
224
257
{
225
258
pass.Apply();
226
259
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList,
227
outlineVertices, 0, vertexIndex / 3);
260
mergedOutlineVertices, 0, vertexIndex / 3);
228
261
}
229
262
}
230
263
231
private static Vector2 GetTrailNormal(IReadOnlyList<Vector2> points,
232
int index, int pointCount)
264
private static int BuildMergedBackdropBatch()
233
265
{
234
Vector2 previous = points[Math.Max(0, index - 1)];
235
Vector2 next = points[Math.Min(pointCount - 1, index + 1)];
236
return (next - previous).SafeNormalize(Vector2.UnitX)
237
.RotatedBy(MathHelper.PiOver2);
266
int vertexIndex = 0;
267
foreach (TrailDrawState state in drawStates.Values)
268
{
269
if (!state.MergeOverlappingRims
270
|| Main.GameUpdateCount > state.UpdateTick + 1
271
|| !IsOnScreen(state.Bounds))
272
{
273
continue;
274
}
275
int pointCount = Math.Min(MaximumPoints, state.Points.Count);
276
Color color = Color.White * state.Opacity;
277
for (int segment = 0; segment < pointCount - 1; segment++)
278
{
279
Vector2 start = state.Points[segment];
280
Vector2 end = state.Points[segment + 1];
281
Vector2 startNormal = GetTrailNormal(state.Points, segment,
282
pointCount);
283
Vector2 endNormal = GetTrailNormal(state.Points, segment + 1,
284
pointCount);
285
float startHalfWidth = state.Width
286
* GetTrailTaper(segment / Math.Max(1f, pointCount - 1f))
287
* 0.5f;
288
float endHalfWidth = state.Width
289
* GetTrailTaper((segment + 1f)
290
/ Math.Max(1f, pointCount - 1f)) * 0.5f;
291
Vector2 leftStart = start - startNormal * startHalfWidth;
292
Vector2 rightStart = start + startNormal * startHalfWidth;
293
Vector2 leftEnd = end - endNormal * endHalfWidth;
294
Vector2 rightEnd = end + endNormal * endHalfWidth;
295
EnsureBatchCapacity(ref mergedBackdropVertices,
296
vertexIndex + 6);
297
WriteBackdropBatchVertex(ref vertexIndex, leftStart, color);
298
WriteBackdropBatchVertex(ref vertexIndex, rightStart, color);
299
WriteBackdropBatchVertex(ref vertexIndex, leftEnd, color);
300
WriteBackdropBatchVertex(ref vertexIndex, leftEnd, color);
301
WriteBackdropBatchVertex(ref vertexIndex, rightStart, color);
302
WriteBackdropBatchVertex(ref vertexIndex, rightEnd, color);
303
}
304
}
305
return vertexIndex;
238
306
}
239
307
240
private static void WriteOutlineVertex(ref int vertexIndex,
308
private static void WriteBackdropBatchVertex(ref int vertexIndex,
241
309
Vector2 world, Color color)
242
310
{
243
outlineVertices[vertexIndex++] = new VertexPositionColorTexture(
244
new Vector3(world - Main.screenPosition, 0f), color, Vector2.Zero);
311
Vector2 uv = new(
312
world.X / DeathDomainBackdropTextureSystem.LayerWidth,
313
world.Y / DeathDomainBackdropTextureSystem.LayerHeight);
314
WriteBatchVertex(mergedBackdropVertices, ref vertexIndex, world,
315
color, uv);
245
316
}
246
317
247
private static bool IsCoveredByAnotherMergedTrail(Vector2 world,
248
long currentVisualId)
318
private static void WriteBatchVertex(VertexPositionColorTexture[] target,
319
ref int vertexIndex, Vector2 world, Color color, Vector2 uv)
249
320
{
250
const float sharedBoundaryTolerance = 1.5f;
251
foreach ((long otherVisualId, TrailDrawState other) in drawStates)
252
{
253
if (otherVisualId == currentVisualId
254
|| !other.MergeOverlappingRims
255
|| Main.GameUpdateCount > other.UpdateTick + 1
256
|| !Contains(other.Bounds, world))
257
{
258
continue;
259
}
321
target[vertexIndex++] = new VertexPositionColorTexture(
322
new Vector3(world - Main.screenPosition, 0f), color, uv);
323
}
260
324
261
float coverage = GetMaximumTrailCoverage(world, other);
262
if (coverage > sharedBoundaryTolerance)
263
return true;
264
if (coverage >= -sharedBoundaryTolerance
265
&& otherVisualId > currentVisualId)
266
{
267
return true;
268
}
269
}
270
return false;
325
private static void EnsureBatchCapacity(
326
ref VertexPositionColorTexture[] target, int required)
327
{
328
if (target.Length >= required)
329
return;
330
int capacity = Math.Max(required, Math.Max(4096, target.Length * 2));
331
Array.Resize(ref target, capacity);
271
332
}
272
333
273
private static float GetMaximumTrailCoverage(Vector2 point,
274
TrailDrawState state)
334
private static Vector2 GetTrailNormal(IReadOnlyList<Vector2> points,
335
int index, int pointCount)
275
336
{
276
float bestCoverage = float.MinValue;
277
int count = Math.Min(MaximumPoints, state.Points.Count);
278
for (int index = 1; index < count; index++)
279
{
280
Vector2 start = state.Points[index - 1];
281
Vector2 delta = state.Points[index] - start;
282
float lengthSquared = delta.LengthSquared();
283
float progress = lengthSquared <= 0.001f ? 0f
284
: MathHelper.Clamp(Vector2.Dot(point - start, delta)
285
/ lengthSquared, 0f, 1f);
286
float trailProgress = (index - 1f + progress)
287
/ Math.Max(1f, count - 1f);
288
float localHalfWidth = state.Width
289
* GetTrailTaper(trailProgress) * 0.5f;
290
float distance = Vector2.Distance(point,
291
start + delta * progress);
292
bestCoverage = Math.Max(bestCoverage, localHalfWidth - distance);
293
}
294
return bestCoverage;
337
Vector2 previous = points[Math.Max(0, index - 1)];
338
Vector2 next = points[Math.Min(pointCount - 1, index + 1)];
339
return (next - previous).SafeNormalize(Vector2.UnitX)
340
.RotatedBy(MathHelper.PiOver2);
295
341
}
296
342
297
343
private static float GetTrailTaper(float progress)
@@ -299,8 +345,8 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
299
345
Math.Sin(MathHelper.Clamp(progress, 0f, 1f) * MathHelper.Pi)),
300
346
0.34f);
301
347
302
private static Vector4 CalculateBounds(IReadOnlyList<Vector2> points,
303
float width)
348
private static Vector4 CalculateTrailBounds(
349
IReadOnlyList<Vector2> points, float width)
304
350
{
305
351
Vector2 minimum = points[0];
306
352
Vector2 maximum = points[0];
@@ -310,14 +356,21 @@ internal sealed class DeathDomainTrailVisualSystem : ModSystem
310
356
minimum = Vector2.Min(minimum, points[index]);
311
357
maximum = Vector2.Max(maximum, points[index]);
312
358
}
313
float padding = width * 0.5f + 2f;
359
float padding = width * 0.5f + 4f;
314
360
return new Vector4(minimum.X - padding, minimum.Y - padding,
315
361
maximum.X + padding, maximum.Y + padding);
316
362
}
317
363
318
private static bool Contains(Vector4 bounds, Vector2 point)
319
=> point.X >= bounds.X && point.Y >= bounds.Y
320
&& point.X <= bounds.Z && point.Y <= bounds.W;
364
private static bool IsOnScreen(Vector4 bounds)
365
{
366
const float padding = 96f;
367
float left = Main.screenPosition.X - padding;
368
float top = Main.screenPosition.Y - padding;
369
float right = Main.screenPosition.X + Main.screenWidth + padding;
370
float bottom = Main.screenPosition.Y + Main.screenHeight + padding;
371
return bounds.Z >= left && bounds.W >= top
372
&& bounds.X <= right && bounds.Y <= bottom;
373
}
321
374
322
375
private static void DrawSolidRibbon(GraphicsDevice graphicsDevice,
323
376
TrailDrawState state, float width, Color color)
@@ -20,8 +20,11 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
20
20
private const int Lifetime = 300;
21
21
private const int MaximumPoints = 72;
22
22
private const float TrailWidth = 30f;
23
private readonly int[] harvestCooldowns = new int[Main.maxNPCs];
23
private readonly ulong[] nextHarvestTicks = new ulong[Main.maxNPCs];
24
24
private readonly List<Vector2> points = [];
25
private readonly HashSet<int> selectedTargets = [];
26
private readonly List<NPC> harvestTargets = [];
27
private Vector4 pathBounds;
25
28
private SickleCombatSnapshot snapshot;
26
29
private int phase;
27
30
private int actionId;
@@ -158,6 +161,7 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
158
161
volleyCounter = incomingVolley;
159
162
points.Clear();
160
163
points.AddRange(incomingPoints);
164
pathBounds = CalculatePathBounds(points);
161
165
configured = points.Count >= 2;
162
166
}
163
167
@@ -167,6 +171,7 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
167
171
snapshot = value;
168
172
points.Clear();
169
173
points.AddRange(path);
174
pathBounds = CalculatePathBounds(points);
170
175
phase = Math.Max(0, sourcePhase);
171
176
actionId = sourceActionId;
172
177
age = 0;
@@ -191,15 +196,13 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
191
196
if (necklace is null)
192
197
return;
193
198
194
for (int index = 0; index < harvestCooldowns.Length; index++)
195
if (harvestCooldowns[index] > 0)
196
harvestCooldowns[index]--;
197
198
HashSet<int> selected = [];
199
List<NPC> targets = [];
199
selectedTargets.Clear();
200
harvestTargets.Clear();
201
ulong currentTick = Main.GameUpdateCount;
200
202
foreach (NPC npc in Main.ActiveNPCs)
201
203
{
202
204
if (!ReaperTargeting.IsValidWeaponTarget(npc)
205
|| !IntersectsPathBounds(npc.Hitbox)
203
206
|| !IntersectsPath(npc.Hitbox))
204
207
{
205
208
continue;
@@ -207,20 +210,21 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
207
210
NPC target = npc.realLife >= 0 && npc.realLife < Main.maxNPCs
208
211
&& Main.npc[npc.realLife].active ? Main.npc[npc.realLife] : npc;
209
212
if (!ReaperTargeting.IsValidWeaponTarget(target)
210
|| !selected.Add(target.whoAmI)
211
|| harvestCooldowns[target.whoAmI] > 0)
213
|| !selectedTargets.Add(target.whoAmI)
214
|| nextHarvestTicks[target.whoAmI] > currentTick)
212
215
{
213
216
continue;
214
217
}
215
harvestCooldowns[target.whoAmI] = necklace.SpawnInterval;
216
targets.Add(target);
218
nextHarvestTicks[target.whoAmI] = currentTick
219
+ (ulong)Math.Max(1, necklace.SpawnInterval);
220
harvestTargets.Add(target);
217
221
}
218
if (targets.Count == 0)
222
if (harvestTargets.Count == 0)
219
223
return;
220
224
221
225
volleyCounter++;
222
226
bool requiem = necklace.DeathRequiemUnlocked && volleyCounter % 4 == 0;
223
foreach (NPC target in targets)
227
foreach (NPC target in harvestTargets)
224
228
{
225
229
uint hash = unchecked((uint)(actionId * 16777619
226
230
+ target.whoAmI * 486187739 + volleyCounter * 97));
@@ -230,7 +234,6 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
230
234
modPlayer.SpawnDeathDomainHarvestFromReaperRift(target, necklace,
231
235
requiem, rotation);
232
236
}
233
Projectile.netUpdate = true;
234
237
}
235
238
236
239
private bool IntersectsPath(Rectangle target)
@@ -247,6 +250,25 @@ public sealed class ReaperDeathDomainRiftProjectile : ModProjectile
247
250
return false;
248
251
}
249
252
253
private bool IntersectsPathBounds(Rectangle target)
254
=> target.Right >= pathBounds.X && target.Bottom >= pathBounds.Y
255
&& target.Left <= pathBounds.Z && target.Top <= pathBounds.W;
256
257
private static Vector4 CalculatePathBounds(IReadOnlyList<Vector2> path)
258
{
259
if (path.Count == 0)
260
return Vector4.Zero;
261
Vector2 minimum = path[0];
262
Vector2 maximum = path[0];
263
for (int index = 1; index < path.Count; index++)
264
{
265
minimum = Vector2.Min(minimum, path[index]);
266
maximum = Vector2.Max(maximum, path[index]);
267
}
268
return new Vector4(minimum.X - TrailWidth, minimum.Y - TrailWidth,
269
maximum.X + TrailWidth, maximum.Y + TrailWidth);
270
}
271
250
272
private float GetOpacity()
251
273
=> Smooth01(age / 8f) * Smooth01(Projectile.timeLeft / 44f);
252
274