tModLoader v0.11.8.9
A mod to make and play Terraria mods
ProjectileLoader.cs
Go to the documentation of this file.
1using Microsoft.Xna.Framework;
2using Microsoft.Xna.Framework.Graphics;
3using System;
4using System.Collections.Generic;
5using System.IO;
6using System.Linq;
7using System.Linq.Expressions;
8using System.Reflection;
9using Terraria.ID;
10using Terraria.Localization;
11
12namespace Terraria.ModLoader
13{
14 //todo: further documentation
18 public static class ProjectileLoader
19 {
20 private static int nextProjectile = ProjectileID.Count;
21 internal static readonly IList<ModProjectile> projectiles = new List<ModProjectile>();
22 internal static readonly IList<GlobalProjectile> globalProjectiles = new List<GlobalProjectile>();
23 internal static GlobalProjectile[] InstancedGlobals = new GlobalProjectile[0];
24 internal static readonly IDictionary<string, int> globalIndexes = new Dictionary<string, int>();
25 internal static readonly IDictionary<Type, int> globalIndexesByType = new Dictionary<Type, int>();
26
27 private class HookList
28 {
30 public readonly MethodInfo method;
31
32 public HookList(MethodInfo method) {
33 this.method = method;
34 }
35 }
36
37 private static List<HookList> hooks = new List<HookList>();
38
39 private static HookList AddHook<F>(Expression<Func<GlobalProjectile, F>> func) {
40 var hook = new HookList(ModLoader.Method(func));
41 hooks.Add(hook);
42 return hook;
43 }
44
45 internal static int ReserveProjectileID() {
46 if (ModNet.AllowVanillaClients) throw new Exception("Adding projectiles breaks vanilla client compatibility");
47
48 int reserveID = nextProjectile;
50 return reserveID;
51 }
52
53 public static int ProjectileCount => nextProjectile;
54
60 public static ModProjectile GetProjectile(int type) {
61 return type >= ProjectileID.Count && type < ProjectileCount ? projectiles[type - ProjectileID.Count] : null;
62 }
63 //change initial size of Terraria.Player.ownedProjectileCounts to ProjectileLoader.ProjectileCount()
64 internal static void ResizeArrays() {
65 Array.Resize(ref Main.projectileLoaded, nextProjectile);
66 Array.Resize(ref Main.projectileTexture, nextProjectile);
67 Array.Resize(ref Main.projHostile, nextProjectile);
68 Array.Resize(ref Main.projHook, nextProjectile);
69 Array.Resize(ref Main.projFrames, nextProjectile);
70 Array.Resize(ref Main.projPet, nextProjectile);
71 Array.Resize(ref Lang._projectileNameCache, nextProjectile);
72 Array.Resize(ref ProjectileID.Sets.YoyosLifeTimeMultiplier, nextProjectile);
73 Array.Resize(ref ProjectileID.Sets.YoyosMaximumRange, nextProjectile);
74 Array.Resize(ref ProjectileID.Sets.YoyosTopSpeed, nextProjectile);
75 Array.Resize(ref ProjectileID.Sets.CanDistortWater, nextProjectile);
76 Array.Resize(ref ProjectileID.Sets.MinionShot, nextProjectile);
77 Array.Resize(ref ProjectileID.Sets.SentryShot, nextProjectile);
78 Array.Resize(ref ProjectileID.Sets.ForcePlateDetection, nextProjectile);
79 Array.Resize(ref ProjectileID.Sets.TrailingMode, nextProjectile);
80 Array.Resize(ref ProjectileID.Sets.TrailCacheLength, nextProjectile);
81 Array.Resize(ref ProjectileID.Sets.LightPet, nextProjectile);
82 Array.Resize(ref ProjectileID.Sets.Homing, nextProjectile);
83 Array.Resize(ref ProjectileID.Sets.IsADD2Turret, nextProjectile);
84 Array.Resize(ref ProjectileID.Sets.TurretFeature, nextProjectile);
85 Array.Resize(ref ProjectileID.Sets.MinionTargettingFeature, nextProjectile);
86 Array.Resize(ref ProjectileID.Sets.MinionSacrificable, nextProjectile);
87 Array.Resize(ref ProjectileID.Sets.DontAttachHideToAlpha, nextProjectile);
88 Array.Resize(ref ProjectileID.Sets.NeedsUUID, nextProjectile);
89 Array.Resize(ref ProjectileID.Sets.StardustDragon, nextProjectile);
90 Array.Resize(ref ProjectileID.Sets.NoLiquidDistortion, nextProjectile);
91 for (int k = ProjectileID.Count; k < nextProjectile; k++) {
92 Lang._projectileNameCache[k] = LocalizedText.Empty;
93 ProjectileID.Sets.YoyosLifeTimeMultiplier[k] = -1;
94 ProjectileID.Sets.YoyosMaximumRange[k] = 200f;
95 ProjectileID.Sets.YoyosTopSpeed[k] = 10f;
96 ProjectileID.Sets.CanDistortWater[k] = true;
97 Main.projectileLoaded[k] = true;
98 Main.projFrames[k] = 1;
99 ProjectileID.Sets.TrailingMode[k] = -1;
100 ProjectileID.Sets.TrailCacheLength[k] = 10;
101 }
102 Array.Resize(ref Projectile.perIDStaticNPCImmunity, nextProjectile);
103 for (int i = 0; i < nextProjectile; i++) {
104 Projectile.perIDStaticNPCImmunity[i] = new uint[200];
105 }
106
107 InstancedGlobals = globalProjectiles.Where(g => g.InstancePerEntity).ToArray();
108 for (int i = 0; i < InstancedGlobals.Length; i++) {
109 InstancedGlobals[i].instanceIndex = i;
110 }
111 foreach (var hook in hooks) {
112 hook.arr = ModLoader.BuildGlobalHook(globalProjectiles, hook.method);
113 }
114 }
115
116 internal static void Unload() {
117 projectiles.Clear();
118 nextProjectile = ProjectileID.Count;
119 globalProjectiles.Clear();
120 globalIndexes.Clear();
121 globalIndexesByType.Clear();
122 }
123
124 internal static bool IsModProjectile(Projectile projectile) {
125 return projectile.type >= ProjectileID.Count;
126 }
127
128 private static HookList HookSetDefaults = AddHook<Action<Projectile>>(g => g.SetDefaults);
129
130 internal static void SetDefaults(Projectile projectile, bool createModProjectile = true) {
131 if (IsModProjectile(projectile) && createModProjectile) {
132 projectile.modProjectile = GetProjectile(projectile.type).NewInstance(projectile);
133 }
134 projectile.globalProjectiles = InstancedGlobals.Select(g => g.NewInstance(projectile)).ToArray();
135 projectile.modProjectile?.SetDefaults();
136 foreach (GlobalProjectile g in HookSetDefaults.arr) {
137 g.Instance(projectile).SetDefaults(projectile);
138 }
139 }
140
141 internal static GlobalProjectile GetGlobalProjectile(Projectile projectile, Mod mod, string name) {
142 int index;
143 return globalIndexes.TryGetValue(mod.Name + ':' + name, out index) ? globalProjectiles[index].Instance(projectile) : null;
144 }
145
146 internal static GlobalProjectile GetGlobalProjectile(Projectile projectile, Type type) {
147 int index;
148 return globalIndexesByType.TryGetValue(type, out index) ? (index > -1 ? globalProjectiles[index].Instance(projectile) : null) : null;
149 }
150 //in Terraria.Projectile rename AI to VanillaAI then make AI call ProjectileLoader.ProjectileAI(this)
151 public static void ProjectileAI(Projectile projectile) {
152 if (PreAI(projectile)) {
153 int type = projectile.type;
154 bool useAiType = projectile.modProjectile != null && projectile.modProjectile.aiType > 0;
155 if (useAiType) {
156 projectile.type = projectile.modProjectile.aiType;
157 }
158 projectile.VanillaAI();
159 if (useAiType) {
160 projectile.type = type;
161 }
162 AI(projectile);
163 }
164 PostAI(projectile);
165 }
166
167 private static HookList HookPreAI = AddHook<Func<Projectile, bool>>(g => g.PreAI);
168
169 public static bool PreAI(Projectile projectile) {
170 bool result = true;
171 foreach (GlobalProjectile g in HookPreAI.arr) {
172 result &= g.Instance(projectile).PreAI(projectile);
173 }
174 if (result && projectile.modProjectile != null) {
175 return projectile.modProjectile.PreAI();
176 }
177 return result;
178 }
179
180 private static HookList HookAI = AddHook<Action<Projectile>>(g => g.AI);
181
182 public static void AI(Projectile projectile) {
183 projectile.modProjectile?.AI();
184
185 foreach (GlobalProjectile g in HookAI.arr) {
186 g.Instance(projectile).AI(projectile);
187 }
188 }
189
190 private static HookList HookPostAI = AddHook<Action<Projectile>>(g => g.PostAI);
191
192 public static void PostAI(Projectile projectile) {
193 projectile.modProjectile?.PostAI();
194
195 foreach (GlobalProjectile g in HookPostAI.arr) {
196 g.Instance(projectile).PostAI(projectile);
197 }
198 }
199 //in Terraria.NetMessage.SendData at end of case 27 call
200 // ProjectileLoader.SendExtraAI(projectile, writer, ref bb14);
201 public static byte[] SendExtraAI(Projectile projectile, ref BitsByte flags) {
202 if (projectile.modProjectile != null) {
203 byte[] data;
204 using (MemoryStream stream = new MemoryStream()) {
205 using (BinaryWriter modWriter = new BinaryWriter(stream)) {
206 projectile.modProjectile.SendExtraAI(modWriter);
207 modWriter.Flush();
208 data = stream.ToArray();
209 }
210 }
211 if (data.Length > 0) {
212 flags[Projectile.maxAI + 1] = true;
213 }
214 return data;
215 }
216 return new byte[0];
217 }
218 //in Terraria.MessageBuffer.GetData for case 27 after reading all data add
219 // byte[] extraAI = ProjectileLoader.ReadExtraAI(reader, bitsByte14);
220 public static byte[] ReadExtraAI(BinaryReader reader, BitsByte flags) {
221 if (flags[Projectile.maxAI + 1]) {
222 return reader.ReadBytes(reader.ReadByte());
223 }
224 return new byte[0];
225 }
226 //in Terraria.MessageBuffer.GetData for case 27 before calling ProjectileFixDesperation add
227 // ProjectileLoader.ReceiveExtraAI(projectile, extraAI);
228 public static void ReceiveExtraAI(Projectile projectile, byte[] extraAI) {
229 if (extraAI.Length > 0 && projectile.modProjectile != null) {
230 using (MemoryStream stream = new MemoryStream(extraAI)) {
231 using (BinaryReader reader = new BinaryReader(stream)) {
232 projectile.modProjectile.ReceiveExtraAI(reader);
233 }
234 }
235 }
236 }
237
238 private static HookList HookShouldUpdatePosition = AddHook<Func<Projectile, bool>>(g => g.ShouldUpdatePosition);
239
240 public static bool ShouldUpdatePosition(Projectile projectile) {
241 if (IsModProjectile(projectile) && !projectile.modProjectile.ShouldUpdatePosition()) {
242 return false;
243 }
245 if (!g.Instance(projectile).ShouldUpdatePosition(projectile)) {
246 return false;
247 }
248 }
249 return true;
250 }
251
252 private delegate bool DelegateTileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough);
253 private static HookList HookTileCollideStyle = AddHook<DelegateTileCollideStyle>(g => g.TileCollideStyle);
254
255 public static bool TileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough) {
256 if (IsModProjectile(projectile) && !projectile.modProjectile.TileCollideStyle(ref width, ref height, ref fallThrough)) {
257 return false;
258 }
259
261 if (!g.Instance(projectile).TileCollideStyle(projectile, ref width, ref height, ref fallThrough)) {
262 return false;
263 }
264 }
265 return true;
266 }
267
268 private static HookList HookOnTileCollide = AddHook<Func<Projectile, Vector2, bool>>(g => g.OnTileCollide);
269
270 public static bool OnTileCollide(Projectile projectile, Vector2 oldVelocity) {
271 bool result = true;
272 foreach (GlobalProjectile g in HookOnTileCollide.arr) {
273 result &= g.Instance(projectile).OnTileCollide(projectile, oldVelocity);
274 }
275 if (result && projectile.modProjectile != null) {
276 return projectile.modProjectile.OnTileCollide(oldVelocity);
277 }
278 return result;
279 }
280
281 private static HookList HookCanCutTiles = AddHook<Func<Projectile, bool?>>(g => g.CanCutTiles);
282
283 public static bool? CanCutTiles(Projectile projectile) {
284 foreach (GlobalProjectile g in HookCanCutTiles.arr) {
285 bool? canCutTiles = g.Instance(projectile).CanCutTiles(projectile);
286 if (canCutTiles.HasValue) {
287 return canCutTiles.Value;
288 }
289 }
290 return projectile.modProjectile?.CanCutTiles();
291 }
292
293 private static HookList HookCutTiles = AddHook<Action<Projectile>>(g => g.CutTiles);
294
295 public static void CutTiles(Projectile projectile) {
296 foreach (GlobalProjectile g in HookCutTiles.arr) {
297 g.Instance(projectile).CutTiles(projectile);
298 }
299 projectile.modProjectile?.CutTiles();
300 }
301
302 private static HookList HookPreKill = AddHook<Func<Projectile, int, bool>>(g => g.PreKill);
303
304 public static bool PreKill(Projectile projectile, int timeLeft) {
305 bool result = true;
306 foreach (GlobalProjectile g in HookPreKill.arr) {
307 result &= g.Instance(projectile).PreKill(projectile, timeLeft);
308 }
309 if (result && projectile.modProjectile != null) {
310 return projectile.modProjectile.PreKill(timeLeft);
311 }
312 return result;
313 }
314
315 private static HookList HookKill = AddHook<Action<Projectile, int>>(g => g.Kill);
316
317 public static void Kill(Projectile projectile, int timeLeft) {
318 projectile.modProjectile?.Kill(timeLeft);
319
320 foreach (GlobalProjectile g in HookKill.arr) {
321 g.Instance(projectile).Kill(projectile, timeLeft);
322 }
323 }
324
325 private static HookList HookCanDamage = AddHook<Func<Projectile, bool>>(g => g.CanDamage);
326
327 public static bool CanDamage(Projectile projectile) {
328 if (projectile.modProjectile != null && !projectile.modProjectile.CanDamage()) {
329 return false;
330 }
331 foreach (GlobalProjectile g in HookCanDamage.arr) {
332 if (!g.Instance(projectile).CanDamage(projectile)) {
333 return false;
334 }
335 }
336 return true;
337 }
338
339 private static HookList HookMinionContactDamage = AddHook<Func<Projectile, bool>>(g => g.MinionContactDamage);
340
341 public static bool MinionContactDamage(Projectile projectile) {
342 if (projectile.modProjectile != null && projectile.modProjectile.MinionContactDamage()) {
343 return true;
344 }
346 if (g.Instance(projectile).MinionContactDamage(projectile)) {
347 return true;
348 }
349 }
350 return false;
351 }
352
353 private delegate void DelegateModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox);
354 private static HookList HookModifyDamageHitbox = AddHook<DelegateModifyDamageHitbox>(g => g.ModifyDamageHitbox);
355
356 public static void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox) {
357 projectile.modProjectile?.ModifyDamageHitbox(ref hitbox);
359 g.Instance(projectile).ModifyDamageHitbox(projectile, ref hitbox);
360 }
361 }
362
363 private static HookList HookCanHitNPC = AddHook<Func<Projectile, NPC, bool?>>(g => g.CanHitNPC);
364
365 public static bool? CanHitNPC(Projectile projectile, NPC target) {
366 bool? flag = null;
367 foreach (GlobalProjectile g in HookCanHitNPC.arr) {
368 bool? canHit = g.Instance(projectile).CanHitNPC(projectile, target);
369 if (canHit.HasValue && !canHit.Value) {
370 return false;
371 }
372 if (canHit.HasValue) {
373 flag = canHit.Value;
374 }
375 }
376 if (projectile.modProjectile != null) {
377 bool? canHit = projectile.modProjectile.CanHitNPC(target);
378 if (canHit.HasValue && !canHit.Value) {
379 return false;
380 }
381 if (canHit.HasValue) {
382 flag = canHit.Value;
383 }
384 }
385 return flag;
386 }
387
388 private delegate void DelegateModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection);
389 private static HookList HookModifyHitNPC = AddHook<DelegateModifyHitNPC>(g => g.ModifyHitNPC);
390
391 public static void ModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection) {
392 projectile.modProjectile?.ModifyHitNPC(target, ref damage, ref knockback, ref crit, ref hitDirection);
393
394 foreach (GlobalProjectile g in HookModifyHitNPC.arr) {
395 g.Instance(projectile).ModifyHitNPC(projectile, target, ref damage, ref knockback, ref crit, ref hitDirection);
396 }
397 }
398
399 private static HookList HookOnHitNPC = AddHook<Action<Projectile, NPC, int, float, bool>>(g => g.OnHitNPC);
400
401 public static void OnHitNPC(Projectile projectile, NPC target, int damage, float knockback, bool crit) {
402 projectile.modProjectile?.OnHitNPC(target, damage, knockback, crit);
403
404 foreach (GlobalProjectile g in HookOnHitNPC.arr) {
405 g.Instance(projectile).OnHitNPC(projectile, target, damage, knockback, crit);
406 }
407 }
408
409 private static HookList HookCanHitPvp = AddHook<Func<Projectile, Player, bool>>(g => g.CanHitPvp);
410
411 public static bool CanHitPvp(Projectile projectile, Player target) {
412 foreach (GlobalProjectile g in HookCanHitPvp.arr) {
413 if (!g.Instance(projectile).CanHitPvp(projectile, target)) {
414 return false;
415 }
416 }
417 if (projectile.modProjectile != null) {
418 return projectile.modProjectile.CanHitPvp(target);
419 }
420 return true;
421 }
422
423 private delegate void DelegateModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit);
424 private static HookList HookModifyHitPvp = AddHook<DelegateModifyHitPvp>(g => g.ModifyHitPvp);
425
426 public static void ModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit) {
427 projectile.modProjectile?.ModifyHitPvp(target, ref damage, ref crit);
428
429 foreach (GlobalProjectile g in HookModifyHitPvp.arr) {
430 g.Instance(projectile).ModifyHitPvp(projectile, target, ref damage, ref crit);
431 }
432 }
433
434 private static HookList HookOnHitPvp = AddHook<Action<Projectile, Player, int, bool>>(g => g.OnHitPvp);
435
436 public static void OnHitPvp(Projectile projectile, Player target, int damage, bool crit) {
437 projectile.modProjectile?.OnHitPvp(target, damage, crit);
438
439 foreach (GlobalProjectile g in HookOnHitPvp.arr) {
440 g.Instance(projectile).OnHitPvp(projectile, target, damage, crit);
441 }
442 }
443
444 private static HookList HookCanHitPlayer = AddHook<Func<Projectile, Player, bool>>(g => g.CanHitPlayer);
445
446 public static bool CanHitPlayer(Projectile projectile, Player target) {
447 foreach (GlobalProjectile g in HookCanHitPlayer.arr) {
448 if (!g.Instance(projectile).CanHitPlayer(projectile, target)) {
449 return false;
450 }
451 }
452 if (projectile.modProjectile != null) {
453 return projectile.modProjectile.CanHitPlayer(target);
454 }
455 return true;
456 }
457
458 private delegate void DelegateModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit);
459 private static HookList HookModifyHitPlayer = AddHook<DelegateModifyHitPlayer>(g => g.ModifyHitPlayer);
460
461 public static void ModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit) {
462 projectile.modProjectile?.ModifyHitPlayer(target, ref damage, ref crit);
463
465 g.Instance(projectile).ModifyHitPlayer(projectile, target, ref damage, ref crit);
466 }
467 }
468
469 private static HookList HookOnHitPlayer = AddHook<Action<Projectile, Player, int, bool>>(g => g.OnHitPlayer);
470
471 public static void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit) {
472 projectile.modProjectile?.OnHitPlayer(target, damage, crit);
473
474 foreach (GlobalProjectile g in HookOnHitPlayer.arr) {
475 g.Instance(projectile).OnHitPlayer(projectile, target, damage, crit);
476 }
477 }
478
479 private static HookList HookColliding = AddHook<Func<Projectile, Rectangle, Rectangle, bool?>>(g => g.Colliding);
480
481 public static bool? Colliding(Projectile projectile, Rectangle projHitbox, Rectangle targetHitbox) {
482 foreach (GlobalProjectile g in HookColliding.arr) {
483 bool? colliding = g.Instance(projectile).Colliding(projectile, projHitbox, targetHitbox);
484 if (colliding.HasValue) {
485 return colliding.Value;
486 }
487 }
488 return projectile.modProjectile?.Colliding(projHitbox, targetHitbox);
489 }
490
491 public static void DrawHeldProjInFrontOfHeldItemAndArms(Projectile projectile, ref bool flag) {
492 if (projectile.modProjectile != null) {
493 flag = projectile.modProjectile.drawHeldProjInFrontOfHeldItemAndArms;
494 }
495 }
496
497 private static HookList HookGetAlpha = AddHook<Func<Projectile, Color, Color?>>(g => g.GetAlpha);
498
499 public static Color? GetAlpha(Projectile projectile, Color lightColor) {
500 foreach (GlobalProjectile g in HookGetAlpha.arr) {
501 Color? color = g.Instance(projectile).GetAlpha(projectile, lightColor);
502 if (color.HasValue) {
503 return color;
504 }
505 }
506 return projectile.modProjectile?.GetAlpha(lightColor);
507 }
508
509 public static void DrawOffset(Projectile projectile, ref int offsetX, ref int offsetY, ref float originX) {
510 if (projectile.modProjectile != null) {
511 offsetX = projectile.modProjectile.drawOffsetX;
512 offsetY = -projectile.modProjectile.drawOriginOffsetY;
513 originX += projectile.modProjectile.drawOriginOffsetX;
514 }
515 }
516
517 private static HookList HookPreDrawExtras = AddHook<Func<Projectile, SpriteBatch, bool>>(g => g.PreDrawExtras);
518
519 public static bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch) {
520 bool result = true;
521 foreach (GlobalProjectile g in HookPreDrawExtras.arr) {
522 result &= g.Instance(projectile).PreDrawExtras(projectile, spriteBatch);
523 }
524 if (result && projectile.modProjectile != null) {
525 return projectile.modProjectile.PreDrawExtras(spriteBatch);
526 }
527 return result;
528 }
529
530 private static HookList HookPreDraw = AddHook<Func<Projectile, SpriteBatch, Color, bool>>(g => g.PreDraw);
531
532 public static bool PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor) {
533 bool result = true;
534 foreach (GlobalProjectile g in HookPreDraw.arr) {
535 result &= g.Instance(projectile).PreDraw(projectile, spriteBatch, lightColor);
536 }
537 if (result && projectile.modProjectile != null) {
538 return projectile.modProjectile.PreDraw(spriteBatch, lightColor);
539 }
540 return result;
541 }
542
543 private static HookList HookPostDraw = AddHook<Action<Projectile, SpriteBatch, Color>>(g => g.PostDraw);
544
545 public static void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor) {
546 projectile.modProjectile?.PostDraw(spriteBatch, lightColor);
547
548 foreach (GlobalProjectile g in HookPostDraw.arr) {
549 g.Instance(projectile).PostDraw(projectile, spriteBatch, lightColor);
550 }
551 }
552
553 private static HookList HookCanUseGrapple = AddHook<Func<int, Player, bool?>>(g => g.CanUseGrapple);
554
555 public static bool? CanUseGrapple(int type, Player player) {
556 var flag = GetProjectile(type)?.CanUseGrapple(player);
557
558 foreach (GlobalProjectile g in HookCanUseGrapple.arr) {
559 bool? canGrapple = g.CanUseGrapple(type, player);
560 if (canGrapple.HasValue) {
561 flag = canGrapple;
562 }
563 }
564 return flag;
565 }
566
567 private static HookList HookSingleGrappleHook = AddHook<Func<int, Player, bool?>>(g => g.SingleGrappleHook);
568
569 public static bool? SingleGrappleHook(int type, Player player) {
570 bool? flag = GetProjectile(type)?.SingleGrappleHook(player);
571
573 bool? singleHook = g.SingleGrappleHook(type, player);
574 if (singleHook.HasValue) {
575 flag = singleHook;
576 }
577 }
578 return flag;
579 }
580
581 private delegate void DelegateUseGrapple(Player player, ref int type);
582 private static HookList HookUseGrapple = AddHook<DelegateUseGrapple>(g => g.UseGrapple);
583
584 public static void UseGrapple(Player player, ref int type) {
585 GetProjectile(type)?.UseGrapple(player, ref type);
586
587 foreach (GlobalProjectile g in HookUseGrapple.arr) {
588 g.UseGrapple(player, ref type);
589 }
590 }
591
592 public static bool GrappleOutOfRange(float distance, Projectile projectile) {
593 return distance > projectile.modProjectile?.GrappleRange();
594 }
595
596 private delegate void DelegateNumGrappleHooks(Projectile projectile, Player player, ref int numHooks);
597 private static HookList HookNumGrappleHooks = AddHook<DelegateNumGrappleHooks>(g => g.NumGrappleHooks);
598
599 public static void NumGrappleHooks(Projectile projectile, Player player, ref int numHooks) {
600 projectile.modProjectile?.NumGrappleHooks(player, ref numHooks);
601
603 g.Instance(projectile).NumGrappleHooks(projectile, player, ref numHooks);
604 }
605 }
606
607 private delegate void DelegateGrappleRetreatSpeed(Projectile projectile, Player player, ref float speed);
608 private static HookList HookGrappleRetreatSpeed = AddHook<DelegateGrappleRetreatSpeed>(g => g.GrappleRetreatSpeed);
609
610 public static void GrappleRetreatSpeed(Projectile projectile, Player player, ref float speed) {
611 projectile.modProjectile?.GrappleRetreatSpeed(player, ref speed);
612
614 g.Instance(projectile).GrappleRetreatSpeed(projectile, player, ref speed);
615 }
616 }
617
618 private delegate void DelegateGrapplePullSpeed(Projectile projectile, Player player, ref float speed);
619 private static HookList HookGrapplePullSpeed = AddHook<DelegateGrapplePullSpeed>(g => g.GrapplePullSpeed);
620
621 public static void GrapplePullSpeed(Projectile projectile, Player player, ref float speed) {
622 projectile.modProjectile?.GrapplePullSpeed(player, ref speed);
623
625 g.Instance(projectile).GrapplePullSpeed(projectile, player, ref speed);
626 }
627 }
628
629 private delegate void DelegateGrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY);
630 private static HookList HookGrappleTargetPoint = AddHook<DelegateGrappleTargetPoint>(g => g.GrappleTargetPoint);
631
632 public static void GrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY) {
633 projectile.modProjectile?.GrappleTargetPoint(player, ref grappleX, ref grappleY);
634
636 g.Instance(projectile).GrappleTargetPoint(projectile, player, ref grappleX, ref grappleY);
637 }
638 }
639
640 private static HookList HookDrawBehind = AddHook<Action<Projectile, int, List<int>, List<int>, List<int>, List<int>>>(g => g.DrawBehind);
641
642 internal static void DrawBehind(Projectile projectile, int index, List<int> drawCacheProjsBehindNPCsAndTiles, List<int> drawCacheProjsBehindNPCs, List<int> drawCacheProjsBehindProjectiles, List<int> drawCacheProjsOverWiresUI) {
643 projectile.modProjectile?.DrawBehind(index, drawCacheProjsBehindNPCsAndTiles, drawCacheProjsBehindNPCs, drawCacheProjsBehindProjectiles, drawCacheProjsOverWiresUI);
644
645 foreach (GlobalProjectile g in HookDrawBehind.arr) {
646 g.Instance(projectile).DrawBehind(projectile, index, drawCacheProjsBehindNPCsAndTiles, drawCacheProjsBehindNPCs, drawCacheProjsBehindProjectiles, drawCacheProjsOverWiresUI);
647 }
648 }
649
650 private static bool HasMethod(Type t, string method, params Type[] args) {
651 return t.GetMethod(method, args).DeclaringType != typeof(GlobalProjectile);
652 }
653
654 internal static void VerifyGlobalProjectile(GlobalProjectile projectile) {
655 var type = projectile.GetType();
656
657 bool hasInstanceFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
658 .Any(f => f.DeclaringType != typeof(GlobalProjectile));
659 if (hasInstanceFields) {
660 if (!projectile.InstancePerEntity) {
661 throw new Exception(type + " has instance fields but does not set InstancePerEntity to true. Either use static fields, or per instance globals");
662 }
663 }
664 }
665 }
666}
This class allows you to modify and use hooks for all projectiles, including vanilla projectiles....
virtual void OnHitPvp(Projectile projectile, Player target, int damage, bool crit)
Allows you to create special effects when a projectile hits an opponent player.
virtual ? bool CanUseGrapple(int type, Player player)
Whether or not a grappling hook that shoots this type of projectile can be used by the given player....
virtual bool InstancePerEntity
Whether to create a new GlobalProjectile instance for every Projectile that exists....
virtual void GrapplePullSpeed(Projectile projectile, Player player, ref float speed)
The speed at which the grapple pulls the player after hitting something. Defaults to 11,...
virtual void Kill(Projectile projectile, int timeLeft)
Allows you to control what happens when a projectile is killed (for example, creating dust or making ...
virtual bool PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
Allows you to draw things behind a projectile, or to modify the way the projectile is drawn....
virtual void ModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit)
Allows you to modify the damage, etc., that a hostile projectile does to a player.
virtual bool PreAI(Projectile projectile)
Allows you to determine how any projectile behaves. Return false to stop the vanilla AI and the AI ho...
virtual void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit)
Allows you to create special effects when a hostile projectile hits a player.
virtual ? bool CanCutTiles(Projectile projectile)
Return true or false to specify if the projectile can cut tiles, like vines. Return null for vanilla ...
virtual void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
Allows you to draw things in front of a projectile. This method is called even if PreDraw returns fal...
virtual bool CanHitPlayer(Projectile projectile, Player target)
Allows you to determine whether a hostile projectile can hit the given player. Return false to block ...
virtual bool OnTileCollide(Projectile projectile, Vector2 oldVelocity)
Allows you to determine what happens when a projectile collides with a tile. OldVelocity is the veloc...
virtual void PostAI(Projectile projectile)
Allows you to determine how any projectile behaves. This will be called regardless of what PreAI retu...
virtual void OnHitNPC(Projectile projectile, NPC target, int damage, float knockback, bool crit)
Allows you to create special effects when a projectile hits an NPC (for example, inflicting debuffs)....
virtual void CutTiles(Projectile projectile)
Code ran when the projectile cuts tiles. Only runs if CanCutTiles() returns true. Useful when program...
virtual void ModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
Allows you to modify the damage, knockback, etc., that a projectile does to an NPC....
virtual ? bool CanHitNPC(Projectile projectile, NPC target)
Allows you to determine whether a projectile can hit the given NPC. Return true to allow hitting the ...
virtual ? Color GetAlpha(Projectile projectile, Color lightColor)
Allows you to determine the color and transparency in which a projectile is drawn....
virtual void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox)
Allows you to change the hitbox used by a projectile for damaging players and NPCs.
virtual bool ShouldUpdatePosition(Projectile projectile)
Whether or not the given projectile should update its position based on factors such as its velocity,...
virtual bool CanHitPvp(Projectile projectile, Player target)
Allows you to determine whether a projectile can hit the given opponent player. Return false to block...
virtual void UseGrapple(Player player, ref int type)
This code is called whenever the player uses a grappling hook that shoots this type of projectile....
virtual bool CanDamage(Projectile projectile)
Whether or not the given projectile is capable of killing tiles (such as grass) and damaging NPCs/pla...
virtual void NumGrappleHooks(Projectile projectile, Player player, ref int numHooks)
How many of this type of grappling hook the given player can latch onto blocks before the hooks start...
virtual bool MinionContactDamage(Projectile projectile)
Whether or not a minion can damage NPCs by touching them. Returns false by default....
virtual ? bool Colliding(Projectile projectile, Rectangle projHitbox, Rectangle targetHitbox)
Allows you to use custom collision detection between a projectile and a player or NPC that the projec...
virtual void ModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit)
Allows you to modify the damage, etc., that a projectile does to an opponent player.
GlobalProjectile Instance(Projectile projectile)
virtual void GrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY)
The location that the grappling hook pulls the player to. Defaults to the center of the hook projecti...
virtual bool PreKill(Projectile projectile, int timeLeft)
Allows you to determine whether the vanilla code for Kill and the Kill hook will be called....
virtual void DrawBehind(Projectile projectile, int index, List< int > drawCacheProjsBehindNPCsAndTiles, List< int > drawCacheProjsBehindNPCs, List< int > drawCacheProjsBehindProjectiles, List< int > drawCacheProjsOverWiresUI)
When used in conjunction with "projectile.hide = true", allows you to specify that this projectile sh...
virtual ? bool SingleGrappleHook(int type, Player player)
Whether or not a grappling hook can only have one hook per player in the world at a time....
virtual void AI(Projectile projectile)
Allows you to determine how any projectile behaves. This will only be called if PreAI returns true.
virtual bool TileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough)
Allows you to determine how a projectile interacts with tiles. Width and height determine the project...
virtual void GrappleRetreatSpeed(Projectile projectile, Player player, ref float speed)
The speed at which the grapple retreats back to the player after not hitting anything....
virtual bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch)
Allows you to draw things behind a projectile. Returns false to stop the game from drawing extras tex...
virtual void SetDefaults(Projectile projectile)
Allows you to set the properties of any and every projectile that gets created.
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
Definition: Mod.cs:25
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
Definition: Mod.cs:42
This serves as the central class which loads mods. It contains many static fields and methods related...
Definition: ModLoader.cs:29
static bool AllowVanillaClients
Definition: ModNet.cs:53
This class serves as a place for you to place all your properties and hooks for each projectile....
virtual ? bool CanUseGrapple(Player player)
This code is called whenever the player uses a grappling hook that shoots this type of projectile....
virtual void UseGrapple(Player player, ref int type)
This code is called whenever the player uses a grappling hook that shoots this type of projectile....
virtual ModProjectile NewInstance(Projectile projectileClone)
Create a new instance of this ModProjectile for a Projectile instance. Called at the end of Projectil...
virtual ? bool SingleGrappleHook(Player player)
Whether or not a grappling hook can only have one hook per player in the world at a time....
This serves as the central class from which projectile-related functions are carried out....
static void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
static bool ShouldUpdatePosition(Projectile projectile)
static void GrappleRetreatSpeed(Projectile projectile, Player player, ref float speed)
static bool PreKill(Projectile projectile, int timeLeft)
static void OnHitNPC(Projectile projectile, NPC target, int damage, float knockback, bool crit)
static void DrawHeldProjInFrontOfHeldItemAndArms(Projectile projectile, ref bool flag)
static ? Color GetAlpha(Projectile projectile, Color lightColor)
static ModProjectile GetProjectile(int type)
Gets the ModProjectile instance corresponding to the specified type.
static void OnHitPvp(Projectile projectile, Player target, int damage, bool crit)
static bool TileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough)
static void GrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY)
delegate void DelegateModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit)
delegate void DelegateGrapplePullSpeed(Projectile projectile, Player player, ref float speed)
static bool GrappleOutOfRange(float distance, Projectile projectile)
static void ProjectileAI(Projectile projectile)
delegate void DelegateUseGrapple(Player player, ref int type)
static ? bool CanCutTiles(Projectile projectile)
static ? bool SingleGrappleHook(int type, Player player)
delegate bool DelegateTileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough)
static void ModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
static bool CanHitPvp(Projectile projectile, Player target)
static void PostAI(Projectile projectile)
static bool CanHitPlayer(Projectile projectile, Player target)
delegate void DelegateModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox)
delegate void DelegateModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
static void DrawOffset(Projectile projectile, ref int offsetX, ref int offsetY, ref float originX)
static bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch)
static bool CanDamage(Projectile projectile)
static void ModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit)
static bool PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
static void GrapplePullSpeed(Projectile projectile, Player player, ref float speed)
static void ModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit)
static void Kill(Projectile projectile, int timeLeft)
static bool OnTileCollide(Projectile projectile, Vector2 oldVelocity)
static ? bool CanUseGrapple(int type, Player player)
delegate void DelegateGrappleRetreatSpeed(Projectile projectile, Player player, ref float speed)
static ? bool Colliding(Projectile projectile, Rectangle projHitbox, Rectangle targetHitbox)
static void CutTiles(Projectile projectile)
static void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox)
static bool MinionContactDamage(Projectile projectile)
static bool PreAI(Projectile projectile)
static byte[] SendExtraAI(Projectile projectile, ref BitsByte flags)
static void UseGrapple(Player player, ref int type)
static void AI(Projectile projectile)
static void ReceiveExtraAI(Projectile projectile, byte[] extraAI)
static byte[] ReadExtraAI(BinaryReader reader, BitsByte flags)
static void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit)
static ? bool CanHitNPC(Projectile projectile, NPC target)
static void NumGrappleHooks(Projectile projectile, Player player, ref int numHooks)
delegate void DelegateModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit)
delegate void DelegateGrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY)
static HookList AddHook< F >(Expression< Func< GlobalProjectile, F > > func)
static bool HasMethod(Type t, string method, params Type[] args)
delegate void DelegateNumGrappleHooks(Projectile projectile, Player player, ref int numHooks)