Terraria ModLoader  0.11.1
A framework for Terraria mods
ProjectileLoader.cs
Go to the documentation of this file.
1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Linq;
7 using System.Linq.Expressions;
8 using System.Reflection;
9 using Terraria.ID;
10 using Terraria.Localization;
11 
12 namespace 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  {
29  public GlobalProjectile[] arr = new GlobalProjectile[0];
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;
49  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  foreach (GlobalProjectile g in HookPreAI.arr) {
171  if (!g.Instance(projectile).PreAI(projectile)) {
172  return false;
173  }
174  }
175  if (projectile.modProjectile != null) {
176  return projectile.modProjectile.PreAI();
177  }
178  return true;
179  }
180 
181  private static HookList HookAI = AddHook<Action<Projectile>>(g => g.AI);
182 
183  public static void AI(Projectile projectile) {
184  projectile.modProjectile?.AI();
185 
186  foreach (GlobalProjectile g in HookAI.arr) {
187  g.Instance(projectile).AI(projectile);
188  }
189  }
190 
191  private static HookList HookPostAI = AddHook<Action<Projectile>>(g => g.PostAI);
192 
193  public static void PostAI(Projectile projectile) {
194  projectile.modProjectile?.PostAI();
195 
196  foreach (GlobalProjectile g in HookPostAI.arr) {
197  g.Instance(projectile).PostAI(projectile);
198  }
199  }
200  //in Terraria.NetMessage.SendData at end of case 27 call
201  // ProjectileLoader.SendExtraAI(projectile, writer, ref bb14);
202  public static byte[] SendExtraAI(Projectile projectile, ref BitsByte flags) {
203  if (projectile.modProjectile != null) {
204  byte[] data;
205  using (MemoryStream stream = new MemoryStream()) {
206  using (BinaryWriter modWriter = new BinaryWriter(stream)) {
207  projectile.modProjectile.SendExtraAI(modWriter);
208  modWriter.Flush();
209  data = stream.ToArray();
210  }
211  }
212  if (data.Length > 0) {
213  flags[Projectile.maxAI + 1] = true;
214  }
215  return data;
216  }
217  return new byte[0];
218  }
219  //in Terraria.MessageBuffer.GetData for case 27 after reading all data add
220  // byte[] extraAI = ProjectileLoader.ReadExtraAI(reader, bitsByte14);
221  public static byte[] ReadExtraAI(BinaryReader reader, BitsByte flags) {
222  if (flags[Projectile.maxAI + 1]) {
223  return reader.ReadBytes(reader.ReadByte());
224  }
225  return new byte[0];
226  }
227  //in Terraria.MessageBuffer.GetData for case 27 before calling ProjectileFixDesperation add
228  // ProjectileLoader.ReceiveExtraAI(projectile, extraAI);
229  public static void ReceiveExtraAI(Projectile projectile, byte[] extraAI) {
230  if (extraAI.Length > 0 && projectile.modProjectile != null) {
231  using (MemoryStream stream = new MemoryStream(extraAI)) {
232  using (BinaryReader reader = new BinaryReader(stream)) {
233  projectile.modProjectile.ReceiveExtraAI(reader);
234  }
235  }
236  }
237  }
238 
239  private static HookList HookShouldUpdatePosition = AddHook<Func<Projectile, bool>>(g => g.ShouldUpdatePosition);
240 
241  public static bool ShouldUpdatePosition(Projectile projectile) {
242  if (IsModProjectile(projectile) && !projectile.modProjectile.ShouldUpdatePosition()) {
243  return false;
244  }
245  foreach (GlobalProjectile g in HookShouldUpdatePosition.arr) {
246  if (!g.Instance(projectile).ShouldUpdatePosition(projectile)) {
247  return false;
248  }
249  }
250  return true;
251  }
252 
253  private delegate bool DelegateTileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough);
254  private static HookList HookTileCollideStyle = AddHook<DelegateTileCollideStyle>(g => g.TileCollideStyle);
255 
256  public static bool TileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough) {
257  if (IsModProjectile(projectile) && !projectile.modProjectile.TileCollideStyle(ref width, ref height, ref fallThrough)) {
258  return false;
259  }
260 
261  foreach (GlobalProjectile g in HookTileCollideStyle.arr) {
262  if (!g.Instance(projectile).TileCollideStyle(projectile, ref width, ref height, ref fallThrough)) {
263  return false;
264  }
265  }
266  return true;
267  }
268 
269  private static HookList HookOnTileCollide = AddHook<Func<Projectile, Vector2, bool>>(g => g.OnTileCollide);
270 
271  public static bool OnTileCollide(Projectile projectile, Vector2 oldVelocity) {
272  foreach (GlobalProjectile g in HookOnTileCollide.arr) {
273  if (!g.Instance(projectile).OnTileCollide(projectile, oldVelocity)) {
274  return false;
275  }
276  }
277  if (projectile.modProjectile != null) {
278  return projectile.modProjectile.OnTileCollide(oldVelocity);
279  }
280  return true;
281  }
282 
283  private static HookList HookCanCutTiles = AddHook<Func<Projectile, bool?>>(g => g.CanCutTiles);
284 
285  public static bool? CanCutTiles(Projectile projectile) {
286  foreach (GlobalProjectile g in HookCanCutTiles.arr) {
287  bool? canCutTiles = g.Instance(projectile).CanCutTiles(projectile);
288  if (canCutTiles.HasValue) {
289  return canCutTiles.Value;
290  }
291  }
292  return projectile.modProjectile?.CanCutTiles();
293  }
294 
295  private static HookList HookCutTiles = AddHook<Action<Projectile>>(g => g.CutTiles);
296 
297  public static void CutTiles(Projectile projectile) {
298  foreach (GlobalProjectile g in HookCutTiles.arr) {
299  g.Instance(projectile).CutTiles(projectile);
300  }
301  projectile.modProjectile?.CutTiles();
302  }
303 
304  private static HookList HookPreKill = AddHook<Func<Projectile, int, bool>>(g => g.PreKill);
305 
306  public static bool PreKill(Projectile projectile, int timeLeft) {
307  foreach (GlobalProjectile g in HookPreKill.arr) {
308  if (!g.Instance(projectile).PreKill(projectile, timeLeft)) {
309  return false;
310  }
311  }
312  if (projectile.modProjectile != null) {
313  return projectile.modProjectile.PreKill(timeLeft);
314  }
315  return true;
316  }
317 
318  private static HookList HookKill = AddHook<Action<Projectile, int>>(g => g.Kill);
319 
320  public static void Kill(Projectile projectile, int timeLeft) {
321  projectile.modProjectile?.Kill(timeLeft);
322 
323  foreach (GlobalProjectile g in HookKill.arr) {
324  g.Instance(projectile).Kill(projectile, timeLeft);
325  }
326  }
327 
328  private static HookList HookCanDamage = AddHook<Func<Projectile, bool>>(g => g.CanDamage);
329 
330  public static bool CanDamage(Projectile projectile) {
331  if (projectile.modProjectile != null && !projectile.modProjectile.CanDamage()) {
332  return false;
333  }
334  foreach (GlobalProjectile g in HookCanDamage.arr) {
335  if (!g.Instance(projectile).CanDamage(projectile)) {
336  return false;
337  }
338  }
339  return true;
340  }
341 
342  private static HookList HookMinionContactDamage = AddHook<Func<Projectile, bool>>(g => g.MinionContactDamage);
343 
344  public static bool MinionContactDamage(Projectile projectile) {
345  if (projectile.modProjectile != null && projectile.modProjectile.MinionContactDamage()) {
346  return true;
347  }
348  foreach (GlobalProjectile g in HookMinionContactDamage.arr) {
349  if (g.Instance(projectile).MinionContactDamage(projectile)) {
350  return true;
351  }
352  }
353  return false;
354  }
355 
356  private delegate void DelegateModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox);
357  private static HookList HookModifyDamageHitbox = AddHook<DelegateModifyDamageHitbox>(g => g.ModifyDamageHitbox);
358 
359  public static void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox) {
360  projectile.modProjectile?.ModifyDamageHitbox(ref hitbox);
361  foreach (GlobalProjectile g in HookModifyDamageHitbox.arr) {
362  g.Instance(projectile).ModifyDamageHitbox(projectile, ref hitbox);
363  }
364  }
365 
366  private static HookList HookCanHitNPC = AddHook<Func<Projectile, NPC, bool?>>(g => g.CanHitNPC);
367 
368  public static bool? CanHitNPC(Projectile projectile, NPC target) {
369  bool? flag = null;
370  foreach (GlobalProjectile g in HookCanHitNPC.arr) {
371  bool? canHit = g.Instance(projectile).CanHitNPC(projectile, target);
372  if (canHit.HasValue && !canHit.Value) {
373  return false;
374  }
375  if (canHit.HasValue) {
376  flag = canHit.Value;
377  }
378  }
379  if (projectile.modProjectile != null) {
380  bool? canHit = projectile.modProjectile.CanHitNPC(target);
381  if (canHit.HasValue && !canHit.Value) {
382  return false;
383  }
384  if (canHit.HasValue) {
385  flag = canHit.Value;
386  }
387  }
388  return flag;
389  }
390 
391  private delegate void DelegateModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection);
392  private static HookList HookModifyHitNPC = AddHook<DelegateModifyHitNPC>(g => g.ModifyHitNPC);
393 
394  public static void ModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection) {
395  projectile.modProjectile?.ModifyHitNPC(target, ref damage, ref knockback, ref crit, ref hitDirection);
396 
397  foreach (GlobalProjectile g in HookModifyHitNPC.arr) {
398  g.Instance(projectile).ModifyHitNPC(projectile, target, ref damage, ref knockback, ref crit, ref hitDirection);
399  }
400  }
401 
402  private static HookList HookOnHitNPC = AddHook<Action<Projectile, NPC, int, float, bool>>(g => g.OnHitNPC);
403 
404  public static void OnHitNPC(Projectile projectile, NPC target, int damage, float knockback, bool crit) {
405  projectile.modProjectile?.OnHitNPC(target, damage, knockback, crit);
406 
407  foreach (GlobalProjectile g in HookOnHitNPC.arr) {
408  g.Instance(projectile).OnHitNPC(projectile, target, damage, knockback, crit);
409  }
410  }
411 
412  private static HookList HookCanHitPvp = AddHook<Func<Projectile, Player, bool>>(g => g.CanHitPvp);
413 
414  public static bool CanHitPvp(Projectile projectile, Player target) {
415  foreach (GlobalProjectile g in HookCanHitPvp.arr) {
416  if (!g.Instance(projectile).CanHitPvp(projectile, target)) {
417  return false;
418  }
419  }
420  if (projectile.modProjectile != null) {
421  return projectile.modProjectile.CanHitPvp(target);
422  }
423  return true;
424  }
425 
426  private delegate void DelegateModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit);
427  private static HookList HookModifyHitPvp = AddHook<DelegateModifyHitPvp>(g => g.ModifyHitPvp);
428 
429  public static void ModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit) {
430  projectile.modProjectile?.ModifyHitPvp(target, ref damage, ref crit);
431 
432  foreach (GlobalProjectile g in HookModifyHitPvp.arr) {
433  g.Instance(projectile).ModifyHitPvp(projectile, target, ref damage, ref crit);
434  }
435  }
436 
437  private static HookList HookOnHitPvp = AddHook<Action<Projectile, Player, int, bool>>(g => g.OnHitPvp);
438 
439  public static void OnHitPvp(Projectile projectile, Player target, int damage, bool crit) {
440  projectile.modProjectile?.OnHitPvp(target, damage, crit);
441 
442  foreach (GlobalProjectile g in HookOnHitPvp.arr) {
443  g.Instance(projectile).OnHitPvp(projectile, target, damage, crit);
444  }
445  }
446 
447  private static HookList HookCanHitPlayer = AddHook<Func<Projectile, Player, bool>>(g => g.CanHitPlayer);
448 
449  public static bool CanHitPlayer(Projectile projectile, Player target) {
450  foreach (GlobalProjectile g in HookCanHitPlayer.arr) {
451  if (!g.Instance(projectile).CanHitPlayer(projectile, target)) {
452  return false;
453  }
454  }
455  if (projectile.modProjectile != null) {
456  return projectile.modProjectile.CanHitPlayer(target);
457  }
458  return true;
459  }
460 
461  private delegate void DelegateModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit);
462  private static HookList HookModifyHitPlayer = AddHook<DelegateModifyHitPlayer>(g => g.ModifyHitPlayer);
463 
464  public static void ModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit) {
465  projectile.modProjectile?.ModifyHitPlayer(target, ref damage, ref crit);
466 
467  foreach (GlobalProjectile g in HookModifyHitPlayer.arr) {
468  g.Instance(projectile).ModifyHitPlayer(projectile, target, ref damage, ref crit);
469  }
470  }
471 
472  private static HookList HookOnHitPlayer = AddHook<Action<Projectile, Player, int, bool>>(g => g.OnHitPlayer);
473 
474  public static void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit) {
475  projectile.modProjectile?.OnHitPlayer(target, damage, crit);
476 
477  foreach (GlobalProjectile g in HookOnHitPlayer.arr) {
478  g.Instance(projectile).OnHitPlayer(projectile, target, damage, crit);
479  }
480  }
481 
482  private static HookList HookColliding = AddHook<Func<Projectile, Rectangle, Rectangle, bool?>>(g => g.Colliding);
483 
484  public static bool? Colliding(Projectile projectile, Rectangle projHitbox, Rectangle targetHitbox) {
485  foreach (GlobalProjectile g in HookColliding.arr) {
486  bool? colliding = g.Instance(projectile).Colliding(projectile, projHitbox, targetHitbox);
487  if (colliding.HasValue) {
488  return colliding.Value;
489  }
490  }
491  return projectile.modProjectile?.Colliding(projHitbox, targetHitbox);
492  }
493 
494  public static void DrawHeldProjInFrontOfHeldItemAndArms(Projectile projectile, ref bool flag) {
495  if (projectile.modProjectile != null) {
496  flag = projectile.modProjectile.drawHeldProjInFrontOfHeldItemAndArms;
497  }
498  }
499 
500  private static HookList HookGetAlpha = AddHook<Func<Projectile, Color, Color?>>(g => g.GetAlpha);
501 
502  public static Color? GetAlpha(Projectile projectile, Color lightColor) {
503  foreach (GlobalProjectile g in HookGetAlpha.arr) {
504  Color? color = g.Instance(projectile).GetAlpha(projectile, lightColor);
505  if (color.HasValue) {
506  return color;
507  }
508  }
509  return projectile.modProjectile?.GetAlpha(lightColor);
510  }
511 
512  public static void DrawOffset(Projectile projectile, ref int offsetX, ref int offsetY, ref float originX) {
513  if (projectile.modProjectile != null) {
514  offsetX = projectile.modProjectile.drawOffsetX;
515  offsetY = -projectile.modProjectile.drawOriginOffsetY;
516  originX += projectile.modProjectile.drawOriginOffsetX;
517  }
518  }
519 
520  private static HookList HookPreDrawExtras = AddHook<Func<Projectile, SpriteBatch, bool>>(g => g.PreDrawExtras);
521 
522  public static bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch) {
523  foreach (GlobalProjectile g in HookPreDrawExtras.arr) {
524  if (!g.Instance(projectile).PreDrawExtras(projectile, spriteBatch)) {
525  return false;
526  }
527  }
528  if (projectile.modProjectile != null) {
529  return projectile.modProjectile.PreDrawExtras(spriteBatch);
530  }
531  return true;
532  }
533 
534  private static HookList HookPreDraw = AddHook<Func<Projectile, SpriteBatch, Color, bool>>(g => g.PreDraw);
535 
536  public static bool PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor) {
537  foreach (GlobalProjectile g in HookPreDraw.arr) {
538  if (!g.Instance(projectile).PreDraw(projectile, spriteBatch, lightColor)) {
539  return false;
540  }
541  }
542  if (projectile.modProjectile != null) {
543  return projectile.modProjectile.PreDraw(spriteBatch, lightColor);
544  }
545  return true;
546  }
547 
548  private static HookList HookPostDraw = AddHook<Action<Projectile, SpriteBatch, Color>>(g => g.PostDraw);
549 
550  public static void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor) {
551  projectile.modProjectile?.PostDraw(spriteBatch, lightColor);
552 
553  foreach (GlobalProjectile g in HookPostDraw.arr) {
554  g.Instance(projectile).PostDraw(projectile, spriteBatch, lightColor);
555  }
556  }
557 
558  private static HookList HookCanUseGrapple = AddHook<Func<int, Player, bool?>>(g => g.CanUseGrapple);
559 
560  public static bool? CanUseGrapple(int type, Player player) {
561  var flag = GetProjectile(type)?.CanUseGrapple(player);
562 
563  foreach (GlobalProjectile g in HookCanUseGrapple.arr) {
564  bool? canGrapple = g.CanUseGrapple(type, player);
565  if (canGrapple.HasValue) {
566  flag = canGrapple;
567  }
568  }
569  return flag;
570  }
571 
572  private static HookList HookSingleGrappleHook = AddHook<Func<int, Player, bool?>>(g => g.SingleGrappleHook);
573 
574  public static bool? SingleGrappleHook(int type, Player player) {
575  bool? flag = GetProjectile(type)?.SingleGrappleHook(player);
576 
577  foreach (GlobalProjectile g in HookSingleGrappleHook.arr) {
578  bool? singleHook = g.SingleGrappleHook(type, player);
579  if (singleHook.HasValue) {
580  flag = singleHook;
581  }
582  }
583  return flag;
584  }
585 
586  private delegate void DelegateUseGrapple(Player player, ref int type);
587  private static HookList HookUseGrapple = AddHook<DelegateUseGrapple>(g => g.UseGrapple);
588 
589  public static void UseGrapple(Player player, ref int type) {
590  GetProjectile(type)?.UseGrapple(player, ref type);
591 
592  foreach (GlobalProjectile g in HookUseGrapple.arr) {
593  g.UseGrapple(player, ref type);
594  }
595  }
596 
597  public static bool GrappleOutOfRange(float distance, Projectile projectile) {
598  return distance > projectile.modProjectile?.GrappleRange();
599  }
600 
601  private delegate void DelegateNumGrappleHooks(Projectile projectile, Player player, ref int numHooks);
602  private static HookList HookNumGrappleHooks = AddHook<DelegateNumGrappleHooks>(g => g.NumGrappleHooks);
603 
604  public static void NumGrappleHooks(Projectile projectile, Player player, ref int numHooks) {
605  projectile.modProjectile?.NumGrappleHooks(player, ref numHooks);
606 
607  foreach (GlobalProjectile g in HookNumGrappleHooks.arr) {
608  g.Instance(projectile).NumGrappleHooks(projectile, player, ref numHooks);
609  }
610  }
611 
612  private delegate void DelegateGrappleRetreatSpeed(Projectile projectile, Player player, ref float speed);
613  private static HookList HookGrappleRetreatSpeed = AddHook<DelegateGrappleRetreatSpeed>(g => g.GrappleRetreatSpeed);
614 
615  public static void GrappleRetreatSpeed(Projectile projectile, Player player, ref float speed) {
616  projectile.modProjectile?.GrappleRetreatSpeed(player, ref speed);
617 
618  foreach (GlobalProjectile g in HookGrappleRetreatSpeed.arr) {
619  g.Instance(projectile).GrappleRetreatSpeed(projectile, player, ref speed);
620  }
621  }
622 
623  private delegate void DelegateGrapplePullSpeed(Projectile projectile, Player player, ref float speed);
624  private static HookList HookGrapplePullSpeed = AddHook<DelegateGrapplePullSpeed>(g => g.GrapplePullSpeed);
625 
626  public static void GrapplePullSpeed(Projectile projectile, Player player, ref float speed) {
627  projectile.modProjectile?.GrapplePullSpeed(player, ref speed);
628 
629  foreach (GlobalProjectile g in HookGrapplePullSpeed.arr) {
630  g.Instance(projectile).GrapplePullSpeed(projectile, player, ref speed);
631  }
632  }
633 
634  private static HookList HookDrawBehind = AddHook<Action<Projectile, int, List<int>, List<int>, List<int>, List<int>>>(g => g.DrawBehind);
635 
636  internal static void DrawBehind(Projectile projectile, int index, List<int> drawCacheProjsBehindNPCsAndTiles, List<int> drawCacheProjsBehindNPCs, List<int> drawCacheProjsBehindProjectiles, List<int> drawCacheProjsOverWiresUI) {
637  projectile.modProjectile?.DrawBehind(index, drawCacheProjsBehindNPCsAndTiles, drawCacheProjsBehindNPCs, drawCacheProjsBehindProjectiles, drawCacheProjsOverWiresUI);
638 
639  foreach (GlobalProjectile g in HookDrawBehind.arr) {
640  g.Instance(projectile).DrawBehind(projectile, index, drawCacheProjsBehindNPCsAndTiles, drawCacheProjsBehindNPCs, drawCacheProjsBehindProjectiles, drawCacheProjsOverWiresUI);
641  }
642  }
643 
644  private static bool HasMethod(Type t, string method, params Type[] args) {
645  return t.GetMethod(method, args).DeclaringType != typeof(GlobalProjectile);
646  }
647 
648  internal static void VerifyGlobalProjectile(GlobalProjectile projectile) {
649  var type = projectile.GetType();
650 
651  bool hasInstanceFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
652  .Any(f => f.DeclaringType != typeof(GlobalProjectile));
653  if (hasInstanceFields) {
654  if (!projectile.InstancePerEntity) {
655  throw new Exception(type + " has instance fields but does not set InstancePerEntity to true. Either use static fields, or per instance globals");
656  }
657  }
658  }
659  }
660 }
static void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox)
static bool HasMethod(Type t, string method, params Type[] args)
static void PostAI(Projectile projectile)
static void Kill(Projectile projectile, int timeLeft)
static bool CanCutTiles(Projectile projectile)
static bool CanHitPlayer(Projectile projectile, Player target)
virtual void Kill(Projectile projectile, int timeLeft)
Allows you to control what happens when a projectile is killed (for example, creating dust or making ...
static Color GetAlpha(Projectile projectile, Color lightColor)
static bool PreKill(Projectile projectile, int timeLeft)
This serves as the central class from which projectile-related functions are carried out...
static bool AllowVanillaClients
Definition: ModNet.cs:52
static byte [] SendExtraAI(Projectile projectile, ref BitsByte flags)
static bool GrappleOutOfRange(float distance, Projectile projectile)
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.
static bool MinionContactDamage(Projectile projectile)
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 void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit)
Allows you to create special effects when a hostile projectile hits a player.
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...
GlobalProjectile Instance(Projectile projectile)
static void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
virtual bool CanCutTiles(Projectile projectile)
Return true or false to specify if the projectile can cut tiles, like vines. Return null for vanilla ...
virtual bool ShouldUpdatePosition(Projectile projectile)
Whether or not the given projectile should update its position based on factors such as its velocity...
This serves as the central class which loads mods. It contains many static fields and methods related...
Definition: ModLoader.cs:24
static ModProjectile GetProjectile(int type)
Gets the ModProjectile instance corresponding to the specified type.
virtual bool CanHitPvp(Projectile projectile, Player target)
Allows you to determine whether a projectile can hit the given opponent player. Return false to block...
static bool ShouldUpdatePosition(Projectile projectile)
virtual bool MinionContactDamage(Projectile projectile)
Whether or not a minion can damage NPCs by touching them. Returns false by default. Note that this will only be used if the projectile is considered a pet.
static byte [] ReadExtraAI(BinaryReader reader, BitsByte flags)
static bool SingleGrappleHook(int type, Player player)
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 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 bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch)
Allows you to draw things behind a projectile. Returns false to stop the game from drawing extras tex...
static void AI(Projectile projectile)
static void OnHitPlayer(Projectile projectile, Player target, int damage, bool crit)
static void OnHitPvp(Projectile projectile, Player target, int damage, bool crit)
static void OnHitNPC(Projectile projectile, NPC target, int damage, float knockback, bool crit)
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 void CutTiles(Projectile projectile)
Code ran when the projectile cuts tiles. Only runs if CanCutTiles() returns true. Useful when program...
virtual bool PreAI(Projectile projectile)
Allows you to determine how any projectile behaves. Return false to stop the vanilla AI and the AI ho...
static void ProjectileAI(Projectile projectile)
static bool PreAI(Projectile projectile)
static void NumGrappleHooks(Projectile projectile, Player player, ref int numHooks)
static bool CanDamage(Projectile projectile)
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...
static bool PreDrawExtras(Projectile projectile, SpriteBatch spriteBatch)
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 void AI(Projectile projectile)
Allows you to determine how any projectile behaves. This will only be called if PreAI returns true...
virtual bool PreKill(Projectile projectile, int timeLeft)
Allows you to determine whether the vanilla code for Kill and the Kill hook will be called...
static void GrappleRetreatSpeed(Projectile projectile, Player player, ref float speed)
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.
static void UseGrapple(Player player, ref int type)
static bool CanHitPvp(Projectile projectile, Player target)
static bool PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
virtual bool CanDamage(Projectile projectile)
Whether or not the given projectile is capable of killing tiles (such as grass) and damaging NPCs/pla...
static void ModifyHitPvp(Projectile projectile, Player target, ref int damage, ref bool crit)
virtual void PostAI(Projectile projectile)
Allows you to determine how any projectile behaves. This will be called regardless of what PreAI retu...
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...
static void CutTiles(Projectile projectile)
virtual bool OnTileCollide(Projectile projectile, Vector2 oldVelocity)
Allows you to determine what happens when a projectile collides with a tile. OldVelocity is the veloc...
static bool OnTileCollide(Projectile projectile, Vector2 oldVelocity)
static bool CanHitNPC(Projectile projectile, NPC target)
virtual string Name
Stores the name of the mod. This name serves as the mod&#39;s identification, and also helps with saving ...
Definition: Mod.cs:41
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...
This class serves as a place for you to place all your properties and hooks for each projectile...
static void ModifyHitPlayer(Projectile projectile, Player target, ref int damage, ref bool crit)
static bool TileCollideStyle(Projectile projectile, ref int width, ref int height, ref bool fallThrough)
Mod is an abstract class that you will override. It serves as a central place from which the mod&#39;s co...
Definition: Mod.cs:23
virtual void ModifyDamageHitbox(Projectile projectile, ref Rectangle hitbox)
Allows you to change the hitbox used by a projectile for damaging players and NPCs.
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. Defaults to 11, but vanilla hooks go up to 24.
static void ModifyHitNPC(Projectile projectile, NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
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 CanHitNPC(Projectile projectile, NPC target)
Allows you to determine whether a projectile can hit the given NPC. Return true to allow hitting the ...
static void DrawOffset(Projectile projectile, ref int offsetX, ref int offsetY, ref float originX)
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 PreDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
Allows you to draw things behind a projectile, or to modify the way the projectile is drawn...
static bool CanUseGrapple(int type, Player player)
static void DrawHeldProjInFrontOfHeldItemAndArms(Projectile projectile, ref bool flag)
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...
This class allows you to modify and use hooks for all projectiles, including vanilla projectiles...
virtual void SetDefaults(Projectile projectile)
Allows you to set the properties of any and every projectile that gets created.
static void ReceiveExtraAI(Projectile projectile, byte[] extraAI)
virtual Color GetAlpha(Projectile projectile, Color lightColor)
Allows you to determine the color and transparency in which a projectile is drawn. Return null to use the default color (normally light and buff color). Returns null by default.
static void GrapplePullSpeed(Projectile projectile, Player player, ref float speed)
virtual bool InstancePerEntity
Whether to create a new GlobalProjectile instance for every Projectile that exists. Useful for storing information on a projectile. Defaults to false. Return true if you need to store information (have non-static fields).
static bool Colliding(Projectile projectile, Rectangle projHitbox, Rectangle targetHitbox)
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.