Terraria ModLoader  0.11.7.5
A mod to make and play 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  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  }
244  foreach (GlobalProjectile g in HookShouldUpdatePosition.arr) {
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 
260  foreach (GlobalProjectile g in HookTileCollideStyle.arr) {
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  }
345  foreach (GlobalProjectile g in HookMinionContactDamage.arr) {
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);
358  foreach (GlobalProjectile g in HookModifyDamageHitbox.arr) {
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 
464  foreach (GlobalProjectile g in HookModifyHitPlayer.arr) {
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 
572  foreach (GlobalProjectile g in HookSingleGrappleHook.arr) {
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 
602  foreach (GlobalProjectile g in HookNumGrappleHooks.arr) {
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 
613  foreach (GlobalProjectile g in HookGrappleRetreatSpeed.arr) {
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 
624  foreach (GlobalProjectile g in HookGrapplePullSpeed.arr) {
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 
635  foreach(GlobalProjectile g in HookGrappleTargetPoint.arr) {
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 }
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:53
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:28
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)
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...
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. This method is only called for the owner of the projectile, meaning that in multi-player, projectiles owned by a player call this method on that client, and projectiles owned by the server such as enemy projectiles call this method on the server.
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 GrappleTargetPoint(Projectile projectile, Player player, ref float grappleX, ref float grappleY)
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:42
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:24
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.