Terraria ModLoader  0.11.1
A framework for Terraria mods
NPCLoader.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.GameContent.UI;
10 using Terraria.ID;
11 using Terraria.Localization;
12 
13 namespace Terraria.ModLoader
14 {
15  //todo: further documentation
19  public static class NPCLoader
20  {
21  internal static bool loaded = false;
22  private static int nextNPC = NPCID.Count;
23  internal static readonly IList<ModNPC> npcs = new List<ModNPC>();
24  internal static readonly IList<GlobalNPC> globalNPCs = new List<GlobalNPC>();
25  internal static GlobalNPC[] InstancedGlobals = new GlobalNPC[0];
26  internal static readonly IDictionary<string, int> globalIndexes = new Dictionary<string, int>();
27  internal static readonly IDictionary<Type, int> globalIndexesByType = new Dictionary<Type, int>();
28  internal static readonly IDictionary<int, int> bannerToItem = new Dictionary<int, int>();
29  private static int vanillaSkeletonCount = NPCID.Sets.Skeletons.Count;
30  private static readonly int[] shopToNPC = new int[Main.MaxShopIDs - 1];
34  public static readonly IList<int> blockLoot = new List<int>();
35 
36  private class HookList
37  {
38  public GlobalNPC[] arr = new GlobalNPC[0];
39  public readonly MethodInfo method;
40 
41  public HookList(MethodInfo method) {
42  this.method = method;
43  }
44  }
45 
46  private static List<HookList> hooks = new List<HookList>();
47 
48  private static HookList AddHook<F>(Expression<Func<GlobalNPC, F>> func) {
49  var hook = new HookList(ModLoader.Method(func));
50  hooks.Add(hook);
51  return hook;
52  }
53 
54  static NPCLoader() {
55  shopToNPC[1] = NPCID.Merchant;
56  shopToNPC[2] = NPCID.ArmsDealer;
57  shopToNPC[3] = NPCID.Dryad;
58  shopToNPC[4] = NPCID.Demolitionist;
59  shopToNPC[5] = NPCID.Clothier;
60  shopToNPC[6] = NPCID.GoblinTinkerer;
61  shopToNPC[7] = NPCID.Wizard;
62  shopToNPC[8] = NPCID.Mechanic;
63  shopToNPC[9] = NPCID.SantaClaus;
64  shopToNPC[10] = NPCID.Truffle;
65  shopToNPC[11] = NPCID.Steampunker;
66  shopToNPC[12] = NPCID.DyeTrader;
67  shopToNPC[13] = NPCID.PartyGirl;
68  shopToNPC[14] = NPCID.Cyborg;
69  shopToNPC[15] = NPCID.Painter;
70  shopToNPC[16] = NPCID.WitchDoctor;
71  shopToNPC[17] = NPCID.Pirate;
72  shopToNPC[18] = NPCID.Stylist;
73  shopToNPC[19] = NPCID.TravellingMerchant;
74  shopToNPC[20] = NPCID.SkeletonMerchant;
75  shopToNPC[21] = NPCID.DD2Bartender;
76  }
77 
78  internal static int ReserveNPCID() {
79  if (ModNet.AllowVanillaClients) throw new Exception("Adding npcs breaks vanilla client compatibility");
80 
81  int reserveID = nextNPC;
82  nextNPC++;
83  return reserveID;
84  }
85 
86  public static int NPCCount => nextNPC;
87 
93  public static ModNPC GetNPC(int type) {
94  return type >= NPCID.Count && type < NPCCount ? npcs[type - NPCID.Count] : null;
95  }
96  //change initial size of Terraria.Player.npcTypeNoAggro and NPCBannerBuff to NPCLoader.NPCCount()
97  //in Terraria.Main.MouseText replace 251 with NPCLoader.NPCCount()
98  //in Terraria.Main.DrawNPCs and Terraria.NPC.NPCLoot remove type too high check
99  //replace a lot of 540 immediates
100  //in Terraria.GameContent.UI.EmoteBubble make CountNPCs internal
101  internal static void ResizeArrays(bool unloading) {
102  Array.Resize(ref Main.NPCLoaded, nextNPC);
103  Array.Resize(ref Main.townNPCCanSpawn, nextNPC);
104  Array.Resize(ref Main.slimeRainNPC, nextNPC);
105  Array.Resize(ref Main.npcTexture, nextNPC);
106  Array.Resize(ref Main.npcAltTextures, nextNPC);
107  Array.Resize(ref Main.npcCatchable, nextNPC);
108  Array.Resize(ref Main.npcFrameCount, nextNPC);
109  Array.Resize(ref NPC.killCount, nextNPC);
110  Array.Resize(ref NPC.npcsFoundForCheckActive, nextNPC);
111  Array.Resize(ref Lang._npcNameCache, nextNPC);
112  Array.Resize(ref EmoteBubble.CountNPCs, nextNPC);
113  Array.Resize(ref WorldGen.TownManager._hasRoom, nextNPC);
114  Array.Resize(ref NPCID.Sets.TrailingMode, nextNPC);
115  Array.Resize(ref NPCID.Sets.BelongsToInvasionOldOnesArmy, nextNPC);
116  Array.Resize(ref NPCID.Sets.TeleportationImmune, nextNPC);
117  Array.Resize(ref NPCID.Sets.UsesNewTargetting, nextNPC);
118  Array.Resize(ref NPCID.Sets.FighterUsesDD2PortalAppearEffect, nextNPC);
119  Array.Resize(ref NPCID.Sets.StatueSpawnedDropRarity, nextNPC);
120  Array.Resize(ref NPCID.Sets.NoEarlymodeLootWhenSpawnedFromStatue, nextNPC);
121  Array.Resize(ref NPCID.Sets.NeedsExpertScaling, nextNPC);
122  Array.Resize(ref NPCID.Sets.ProjectileNPC, nextNPC);
123  Array.Resize(ref NPCID.Sets.SavesAndLoads, nextNPC);
124  Array.Resize(ref NPCID.Sets.TrailCacheLength, nextNPC);
125  Array.Resize(ref NPCID.Sets.MPAllowedEnemies, nextNPC);
126  Array.Resize(ref NPCID.Sets.TownCritter, nextNPC);
127  Array.Resize(ref NPCID.Sets.HatOffsetY, nextNPC);
128  Array.Resize(ref NPCID.Sets.FaceEmote, nextNPC);
129  Array.Resize(ref NPCID.Sets.ExtraFramesCount, nextNPC);
130  Array.Resize(ref NPCID.Sets.AttackFrameCount, nextNPC);
131  Array.Resize(ref NPCID.Sets.DangerDetectRange, nextNPC);
132  Array.Resize(ref NPCID.Sets.AttackTime, nextNPC);
133  Array.Resize(ref NPCID.Sets.AttackAverageChance, nextNPC);
134  Array.Resize(ref NPCID.Sets.AttackType, nextNPC);
135  Array.Resize(ref NPCID.Sets.PrettySafe, nextNPC);
136  Array.Resize(ref NPCID.Sets.MagicAuraColor, nextNPC);
137  Array.Resize(ref NPCID.Sets.BossHeadTextures, nextNPC);
138  Array.Resize(ref NPCID.Sets.ExcludedFromDeathTally, nextNPC);
139  Array.Resize(ref NPCID.Sets.TechnicallyABoss, nextNPC);
140  Array.Resize(ref NPCID.Sets.MustAlwaysDraw, nextNPC);
141  Array.Resize(ref NPCID.Sets.ExtraTextureCount, nextNPC);
142  Array.Resize(ref NPCID.Sets.NPCFramingGroup, nextNPC);
143  Array.Resize(ref NPCID.Sets.TownNPCsFramingGroups, nextNPC);
144  for (int k = NPCID.Count; k < nextNPC; k++) {
145  Main.NPCLoaded[k] = true;
146  Main.npcFrameCount[k] = 1;
147  Lang._npcNameCache[k] = LocalizedText.Empty;
148  NPCID.Sets.TrailingMode[k] = -1;
149  NPCID.Sets.StatueSpawnedDropRarity[k] = -1f;
150  NPCID.Sets.TrailCacheLength[k] = 10;
151  NPCID.Sets.DangerDetectRange[k] = -1;
152  NPCID.Sets.AttackTime[k] = -1;
153  NPCID.Sets.AttackAverageChance[k] = 1;
154  NPCID.Sets.AttackType[k] = -1;
155  NPCID.Sets.PrettySafe[k] = -1;
156  NPCID.Sets.MagicAuraColor[k] = Color.White;
157  NPCID.Sets.BossHeadTextures[k] = -1;
158  }
159 
160  InstancedGlobals = globalNPCs.Where(g => g.InstancePerEntity).ToArray();
161  for (int i = 0; i < InstancedGlobals.Length; i++) {
162  InstancedGlobals[i].instanceIndex = i;
163  }
164  foreach (var hook in hooks) {
165  hook.arr = ModLoader.BuildGlobalHook(globalNPCs, hook.method);
166  }
167 
168  if (!unloading) {
169  loaded = true;
170  }
171  }
172 
173  internal static void Unload() {
174  loaded = false;
175  npcs.Clear();
176  nextNPC = NPCID.Count;
177  globalNPCs.Clear();
178  globalIndexes.Clear();
179  globalIndexesByType.Clear();
180  bannerToItem.Clear();
181  while (NPCID.Sets.Skeletons.Count > vanillaSkeletonCount) {
182  NPCID.Sets.Skeletons.RemoveAt(NPCID.Sets.Skeletons.Count - 1);
183  }
184  }
185 
186  internal static bool IsModNPC(NPC npc) {
187  return npc.type >= NPCID.Count;
188  }
189 
190  private static HookList HookSetDefaults = AddHook<Action<NPC>>(g => g.SetDefaults);
191 
192  internal static void SetDefaults(NPC npc, bool createModNPC = true) {
193  if (IsModNPC(npc)) {
194  if (createModNPC) {
195  npc.modNPC = GetNPC(npc.type).NewInstance(npc);
196  }
197  else //the default NPCs created and bound to ModNPCs are initialized before ResizeArrays. They come here during SetupContent.
198  {
199  Array.Resize(ref npc.buffImmune, BuffLoader.BuffCount);
200  }
201  }
202  npc.globalNPCs = InstancedGlobals.Select(g => g.NewInstance(npc)).ToArray();
203  npc.modNPC?.SetDefaults();
204  foreach (GlobalNPC g in HookSetDefaults.arr) {
205  g.Instance(npc).SetDefaults(npc);
206  }
207  }
208 
209  internal static GlobalNPC GetGlobalNPC(NPC npc, Mod mod, string name) {
210  int index;
211  return globalIndexes.TryGetValue(mod.Name + ':' + name, out index) ? globalNPCs[index].Instance(npc) : null;
212  }
213 
214  internal static GlobalNPC GetGlobalNPC(NPC npc, Type type) {
215  int index;
216  return globalIndexesByType.TryGetValue(type, out index) ? (index > -1 ? globalNPCs[index].Instance(npc) : null) : null;
217  }
218 
219  private static HookList HookScaleExpertStats = AddHook<Action<NPC, int, float>>(g => g.ScaleExpertStats);
220 
221  public static void ScaleExpertStats(NPC npc, int numPlayers, float bossLifeScale) {
222  npc.modNPC?.ScaleExpertStats(numPlayers, bossLifeScale);
223 
224  foreach (GlobalNPC g in HookScaleExpertStats.arr) {
225  g.Instance(npc).ScaleExpertStats(npc, numPlayers, bossLifeScale);
226  }
227  }
228 
229  private static HookList HookResetEffects = AddHook<Action<NPC>>(g => g.ResetEffects);
230 
231  public static void ResetEffects(NPC npc) {
232  npc.modNPC?.ResetEffects();
233 
234  foreach (GlobalNPC g in HookResetEffects.arr) {
235  g.Instance(npc).ResetEffects(npc);
236  }
237  }
238 
239  public static void NPCAI(NPC npc) {
240  if (PreAI(npc)) {
241  int type = npc.type;
242  bool useAiType = npc.modNPC != null && npc.modNPC.aiType > 0;
243  if (useAiType) {
244  npc.type = npc.modNPC.aiType;
245  }
246  npc.VanillaAI();
247  if (useAiType) {
248  npc.type = type;
249  }
250  AI(npc);
251  }
252  PostAI(npc);
253  }
254 
255  private static HookList HookPreAI = AddHook<Func<NPC, bool>>(g => g.PreAI);
256 
257  public static bool PreAI(NPC npc) {
258  foreach (GlobalNPC g in HookPreAI.arr) {
259  if (!g.Instance(npc).PreAI(npc)) {
260  return false;
261  }
262  }
263  if (npc.modNPC != null) {
264  return npc.modNPC.PreAI();
265  }
266  return true;
267  }
268 
269  private static HookList HookAI = AddHook<Action<NPC>>(g => g.AI);
270 
271  public static void AI(NPC npc) {
272  npc.modNPC?.AI();
273 
274  foreach (GlobalNPC g in HookAI.arr) {
275  g.Instance(npc).AI(npc);
276  }
277  }
278 
279  private static HookList HookPostAI = AddHook<Action<NPC>>(g => g.PostAI);
280 
281  public static void PostAI(NPC npc) {
282  npc.modNPC?.PostAI();
283 
284  foreach (GlobalNPC g in HookPostAI.arr) {
285  g.Instance(npc).PostAI(npc);
286  }
287  }
288 
289  public static void SendExtraAI(NPC npc, BinaryWriter writer) {
290  if (npc.modNPC != null) {
291  byte[] data;
292  using (MemoryStream stream = new MemoryStream()) {
293  using (BinaryWriter modWriter = new BinaryWriter(stream)) {
294  npc.modNPC.SendExtraAI(modWriter);
295  modWriter.Flush();
296  data = stream.ToArray();
297  }
298  }
299  writer.Write((byte)data.Length);
300  if (data.Length > 0) {
301  writer.Write(data);
302  }
303  }
304  }
305 
306  public static void ReceiveExtraAI(NPC npc, BinaryReader reader) {
307  if (npc.modNPC != null) {
308  byte[] extraAI = reader.ReadBytes(reader.ReadByte());
309  if (extraAI.Length > 0) {
310  using (MemoryStream stream = new MemoryStream(extraAI)) {
311  using (BinaryReader modReader = new BinaryReader(stream)) {
312  npc.modNPC.ReceiveExtraAI(modReader);
313  }
314  }
315  }
316  }
317  }
318 
319  private static HookList HookFindFrame = AddHook<Action<NPC, int>>(g => g.FindFrame);
320 
321  public static void FindFrame(NPC npc, int frameHeight) {
322  int type = npc.type;
323  if (npc.modNPC != null && npc.modNPC.animationType > 0) {
324  npc.type = npc.modNPC.animationType;
325  }
326  npc.VanillaFindFrame(frameHeight);
327  npc.type = type;
328  npc.modNPC?.FindFrame(frameHeight);
329 
330  foreach (GlobalNPC g in HookFindFrame.arr) {
331  g.Instance(npc).FindFrame(npc, frameHeight);
332  }
333  }
334 
335  private static HookList HookHitEffect = AddHook<Action<NPC, int, double>>(g => g.HitEffect);
336 
337  public static void HitEffect(NPC npc, int hitDirection, double damage) {
338  npc.VanillaHitEffect(hitDirection, damage);
339  npc.modNPC?.HitEffect(hitDirection, damage);
340 
341  foreach (GlobalNPC g in HookHitEffect.arr) {
342  g.Instance(npc).HitEffect(npc, hitDirection, damage);
343  }
344  }
345 
346  private delegate void DelegateUpdateLifeRegen(NPC npc, ref int damage);
347  private static HookList HookUpdateLifeRegen = AddHook<DelegateUpdateLifeRegen>(g => g.UpdateLifeRegen);
348 
349  public static void UpdateLifeRegen(NPC npc, ref int damage) {
350  npc.modNPC?.UpdateLifeRegen(ref damage);
351 
352  foreach (GlobalNPC g in HookUpdateLifeRegen.arr) {
353  g.Instance(npc).UpdateLifeRegen(npc, ref damage);
354  }
355  }
356 
357  private static HookList HookCheckActive = AddHook<Func<NPC, bool>>(g => g.CheckActive);
358 
359  public static bool CheckActive(NPC npc) {
360  if (npc.modNPC != null && !npc.modNPC.CheckActive()) {
361  return false;
362  }
363  foreach (GlobalNPC g in HookCheckActive.arr) {
364  if (!g.Instance(npc).CheckActive(npc)) {
365  return false;
366  }
367  }
368  return true;
369  }
370 
371  private static HookList HookCheckDead = AddHook<Func<NPC, bool>>(g => g.CheckDead);
372 
373  public static bool CheckDead(NPC npc) {
374  if (npc.modNPC != null && !npc.modNPC.CheckDead()) {
375  return false;
376  }
377  foreach (GlobalNPC g in HookCheckDead.arr) {
378  if (!g.Instance(npc).CheckDead(npc)) {
379  return false;
380  }
381  }
382  return true;
383  }
384 
385  private static HookList HookSpecialNPCLoot = AddHook<Func<NPC, bool>>(g => g.SpecialNPCLoot);
386 
387  public static bool SpecialNPCLoot(NPC npc) {
388  foreach (GlobalNPC g in HookSpecialNPCLoot.arr) {
389  if (g.Instance(npc).SpecialNPCLoot(npc)) {
390  return true;
391  }
392  }
393  if (npc.modNPC != null) {
394  return npc.modNPC.SpecialNPCLoot();
395  }
396  return false;
397  }
398 
399  private static HookList HookPreNPCLoot = AddHook<Func<NPC, bool>>(g => g.PreNPCLoot);
400 
401  public static bool PreNPCLoot(NPC npc) {
402  foreach (GlobalNPC g in HookPreNPCLoot.arr) {
403  if (!g.Instance(npc).PreNPCLoot(npc)) {
404  blockLoot.Clear();
405  return false;
406  }
407  }
408  if (npc.modNPC != null && !npc.modNPC.PreNPCLoot()) {
409  blockLoot.Clear();
410  return false;
411  }
412  return true;
413  }
414 
415  private static HookList HookNPCLoot = AddHook<Action<NPC>>(g => g.NPCLoot);
416 
417  public static void NPCLoot(NPC npc) {
418  npc.modNPC?.NPCLoot();
419 
420  foreach (GlobalNPC g in HookNPCLoot.arr) {
421  g.Instance(npc).NPCLoot(npc);
422  }
423  blockLoot.Clear();
424  }
425 
426  public static void BossLoot(NPC npc, ref string name, ref int potionType) {
427  npc.modNPC?.BossLoot(ref name, ref potionType);
428  }
429 
430  public static void BossBag(NPC npc, ref int bagType) {
431  if (npc.modNPC != null) {
432  bagType = npc.modNPC.bossBag;
433  }
434  }
435 
436  private delegate bool DelegateCanHitPlayer(NPC npc, Player target, ref int cooldownSlot);
437  private static HookList HookCanHitPlayer = AddHook<DelegateCanHitPlayer>(g => g.CanHitPlayer);
438 
439  public static bool CanHitPlayer(NPC npc, Player target, ref int cooldownSlot) {
440  foreach (GlobalNPC g in HookCanHitPlayer.arr) {
441  if (!g.Instance(npc).CanHitPlayer(npc, target, ref cooldownSlot)) {
442  return false;
443  }
444  }
445  if (npc.modNPC != null) {
446  return npc.modNPC.CanHitPlayer(target, ref cooldownSlot);
447  }
448  return true;
449  }
450 
451  private delegate void DelegateModifyHitPlayer(NPC npc, Player target, ref int damage, ref bool crit);
452  private static HookList HookModifyHitPlayer = AddHook<DelegateModifyHitPlayer>(g => g.ModifyHitPlayer);
453 
454  public static void ModifyHitPlayer(NPC npc, Player target, ref int damage, ref bool crit) {
455  npc.modNPC?.ModifyHitPlayer(target, ref damage, ref crit);
456 
457  foreach (GlobalNPC g in HookModifyHitPlayer.arr) {
458  g.Instance(npc).ModifyHitPlayer(npc, target, ref damage, ref crit);
459  }
460  }
461 
462  private static HookList HookOnHitPlayer = AddHook<Action<NPC, Player, int, bool>>(g => g.OnHitPlayer);
463 
464  public static void OnHitPlayer(NPC npc, Player target, int damage, bool crit) {
465  npc.modNPC?.OnHitPlayer(target, damage, crit);
466 
467  foreach (GlobalNPC g in HookOnHitPlayer.arr) {
468  g.Instance(npc).OnHitPlayer(npc, target, damage, crit);
469  }
470  }
471 
472  private static HookList HookCanHitNPC = AddHook<Func<NPC, NPC, bool?>>(g => g.CanHitNPC);
473 
474  public static bool? CanHitNPC(NPC npc, NPC target) {
475  bool? flag = null;
476  foreach (GlobalNPC g in HookCanHitNPC.arr) {
477  bool? canHit = g.Instance(npc).CanHitNPC(npc, target);
478  if (canHit.HasValue && !canHit.Value) {
479  return false;
480  }
481  if (canHit.HasValue) {
482  flag = canHit.Value;
483  }
484  }
485  if (npc.modNPC != null) {
486  bool? canHit = npc.modNPC.CanHitNPC(target);
487  if (canHit.HasValue && !canHit.Value) {
488  return false;
489  }
490  if (canHit.HasValue) {
491  flag = canHit.Value;
492  }
493  }
494  return flag;
495  }
496 
497  private delegate void DelegateModifyHitNPC(NPC npc, NPC target, ref int damage, ref float knockback, ref bool crit);
498  private static HookList HookModifyHitNPC = AddHook<DelegateModifyHitNPC>(g => g.ModifyHitNPC);
499 
500  public static void ModifyHitNPC(NPC npc, NPC target, ref int damage, ref float knockback, ref bool crit) {
501  npc.modNPC?.ModifyHitNPC(target, ref damage, ref knockback, ref crit);
502 
503  foreach (GlobalNPC g in HookModifyHitNPC.arr) {
504  g.Instance(npc).ModifyHitNPC(npc, target, ref damage, ref knockback, ref crit);
505  }
506  }
507 
508  private static HookList HookOnHitNPC = AddHook<Action<NPC, NPC, int, float, bool>>(g => g.OnHitNPC);
509 
510  public static void OnHitNPC(NPC npc, NPC target, int damage, float knockback, bool crit) {
511  npc.modNPC?.OnHitNPC(target, damage, knockback, crit);
512  foreach (GlobalNPC g in HookOnHitNPC.arr) {
513  g.Instance(npc).OnHitNPC(npc, target, damage, knockback, crit);
514  }
515  }
516 
517  private static HookList HookCanBeHitByItem = AddHook<Func<NPC, Player, Item, bool?>>(g => g.CanBeHitByItem);
518 
519  public static bool? CanBeHitByItem(NPC npc, Player player, Item item) {
520  bool? flag = null;
521  foreach (GlobalNPC g in HookCanBeHitByItem.arr) {
522  bool? canHit = g.Instance(npc).CanBeHitByItem(npc, player, item);
523  if (canHit.HasValue && !canHit.Value) {
524  return false;
525  }
526  if (canHit.HasValue) {
527  flag = canHit.Value;
528  }
529  }
530  if (npc.modNPC != null) {
531  bool? canHit = npc.modNPC.CanBeHitByItem(player, item);
532  if (canHit.HasValue && !canHit.Value) {
533  return false;
534  }
535  if (canHit.HasValue) {
536  flag = canHit.Value;
537  }
538  }
539  return flag;
540  }
541 
542  private delegate void DelegateModifyHitByItem(NPC npc, Player player, Item item, ref int damage, ref float knockback, ref bool crit);
543  private static HookList HookModifyHitByItem = AddHook<DelegateModifyHitByItem>(g => g.ModifyHitByItem);
544 
545  public static void ModifyHitByItem(NPC npc, Player player, Item item, ref int damage, ref float knockback, ref bool crit) {
546  npc.modNPC?.ModifyHitByItem(player, item, ref damage, ref knockback, ref crit);
547 
548  foreach (GlobalNPC g in HookModifyHitByItem.arr) {
549  g.Instance(npc).ModifyHitByItem(npc, player, item, ref damage, ref knockback, ref crit);
550  }
551  }
552 
553  private static HookList HookOnHitByItem = AddHook<Action<NPC, Player, Item, int, float, bool>>(g => g.OnHitByItem);
554 
555  public static void OnHitByItem(NPC npc, Player player, Item item, int damage, float knockback, bool crit) {
556  npc.modNPC?.OnHitByItem(player, item, damage, knockback, crit);
557 
558  foreach (GlobalNPC g in HookOnHitByItem.arr) {
559  g.Instance(npc).OnHitByItem(npc, player, item, damage, knockback, crit);
560  }
561  }
562 
563  private static HookList HookCanBeHitByProjectile = AddHook<Func<NPC, Projectile, bool?>>(g => g.CanBeHitByProjectile);
564 
565  public static bool? CanBeHitByProjectile(NPC npc, Projectile projectile) {
566  bool? flag = null;
567  foreach (GlobalNPC g in HookCanBeHitByProjectile.arr) {
568  bool? canHit = g.Instance(npc).CanBeHitByProjectile(npc, projectile);
569  if (canHit.HasValue && !canHit.Value) {
570  return false;
571  }
572  if (canHit.HasValue) {
573  flag = canHit.Value;
574  }
575  }
576  if (npc.modNPC != null) {
577  bool? canHit = npc.modNPC.CanBeHitByProjectile(projectile);
578  if (canHit.HasValue && !canHit.Value) {
579  return false;
580  }
581  if (canHit.HasValue) {
582  flag = canHit.Value;
583  }
584  }
585  return flag;
586  }
587 
588  private delegate void DelegateModifyHitByProjectile(NPC npc, Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection);
589  private static HookList HookModifyHitByProjectile = AddHook<DelegateModifyHitByProjectile>(g => g.ModifyHitByProjectile);
590 
591  public static void ModifyHitByProjectile(NPC npc, Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection) {
592  npc.modNPC?.ModifyHitByProjectile(projectile, ref damage, ref knockback, ref crit, ref hitDirection);
593 
594  foreach (GlobalNPC g in HookModifyHitByProjectile.arr) {
595  g.Instance(npc).ModifyHitByProjectile(npc, projectile, ref damage, ref knockback, ref crit, ref hitDirection);
596  }
597  }
598 
599  private static HookList HookOnHitByProjectile = AddHook<Action<NPC, Projectile, int, float, bool>>(g => g.OnHitByProjectile);
600 
601  public static void OnHitByProjectile(NPC npc, Projectile projectile, int damage, float knockback, bool crit) {
602  npc.modNPC?.OnHitByProjectile(projectile, damage, knockback, crit);
603 
604  foreach (GlobalNPC g in HookOnHitByProjectile.arr) {
605  g.Instance(npc).OnHitByProjectile(npc, projectile, damage, knockback, crit);
606  }
607  }
608 
609  private delegate bool DelegateStrikeNPC(NPC npc, ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit);
610  private static HookList HookStrikeNPC = AddHook<DelegateStrikeNPC>(g => g.StrikeNPC);
611 
612  public static bool StrikeNPC(NPC npc, ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit) {
613  bool flag = true;
614  if (npc.modNPC != null) {
615  flag = npc.modNPC.StrikeNPC(ref damage, defense, ref knockback, hitDirection, ref crit);
616  }
617  foreach (GlobalNPC g in HookStrikeNPC.arr) {
618  if (!g.Instance(npc).StrikeNPC(npc, ref damage, defense, ref knockback, hitDirection, ref crit)) {
619  flag = false;
620  }
621  }
622  return flag;
623  }
624 
625  private delegate void DelegateBossHeadSlot(NPC npc, ref int index);
626  private static HookList HookBossHeadSlot = AddHook<DelegateBossHeadSlot>(g => g.BossHeadSlot);
627 
628  public static void BossHeadSlot(NPC npc, ref int index) {
629  npc.modNPC?.BossHeadSlot(ref index);
630 
631  foreach (GlobalNPC g in HookBossHeadSlot.arr) {
632  g.Instance(npc).BossHeadSlot(npc, ref index);
633  }
634  }
635 
636  private delegate void DelegateBossHeadRotation(NPC npc, ref float rotation);
637  private static HookList HookBossHeadRotation = AddHook<DelegateBossHeadRotation>(g => g.BossHeadRotation);
638 
639  public static void BossHeadRotation(NPC npc, ref float rotation) {
640  npc.modNPC?.BossHeadRotation(ref rotation);
641 
642  foreach (GlobalNPC g in HookBossHeadRotation.arr) {
643  g.Instance(npc).BossHeadRotation(npc, ref rotation);
644  }
645  }
646 
647  private delegate void DelegateBossHeadSpriteEffects(NPC npc, ref SpriteEffects spriteEffects);
648  private static HookList HookBossHeadSpriteEffects = AddHook<DelegateBossHeadSpriteEffects>(g => g.BossHeadSpriteEffects);
649 
650  public static void BossHeadSpriteEffects(NPC npc, ref SpriteEffects spriteEffects) {
651  npc.modNPC?.BossHeadSpriteEffects(ref spriteEffects);
652 
653  foreach (GlobalNPC g in HookBossHeadSpriteEffects.arr) {
654  g.Instance(npc).BossHeadSpriteEffects(npc, ref spriteEffects);
655  }
656  }
657 
658  private static HookList HookGetAlpha = AddHook<Func<NPC, Color, Color?>>(g => g.GetAlpha);
659 
660  public static Color? GetAlpha(NPC npc, Color lightColor) {
661  foreach (GlobalNPC g in HookGetAlpha.arr) {
662  Color? color = g.Instance(npc).GetAlpha(npc, lightColor);
663  if (color.HasValue) {
664  return color.Value;
665  }
666  }
667  return npc.modNPC?.GetAlpha(lightColor);
668  }
669 
670  private delegate void DelegateDrawEffects(NPC npc, ref Color drawColor);
671  private static HookList HookDrawEffects = AddHook<DelegateDrawEffects>(g => g.DrawEffects);
672 
673  public static void DrawEffects(NPC npc, ref Color drawColor) {
674  npc.modNPC?.DrawEffects(ref drawColor);
675 
676  foreach (GlobalNPC g in HookDrawEffects.arr) {
677  g.Instance(npc).DrawEffects(npc, ref drawColor);
678  }
679  }
680 
681  private static HookList HookPreDraw = AddHook<Func<NPC, SpriteBatch, Color, bool>>(g => g.PreDraw);
682 
683  public static bool PreDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor) {
684  foreach (GlobalNPC g in HookPreDraw.arr) {
685  if (!g.Instance(npc).PreDraw(npc, spriteBatch, drawColor)) {
686  return false;
687  }
688  }
689  if (npc.modNPC != null) {
690  return npc.modNPC.PreDraw(spriteBatch, drawColor);
691  }
692  return true;
693  }
694 
695  private static HookList HookPostDraw = AddHook<Action<NPC, SpriteBatch, Color>>(g => g.PostDraw);
696 
697  public static void PostDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor) {
698  npc.modNPC?.PostDraw(spriteBatch, drawColor);
699 
700  foreach (GlobalNPC g in HookPostDraw.arr) {
701  g.Instance(npc).PostDraw(npc, spriteBatch, drawColor);
702  }
703  }
704 
705  private delegate bool? DelegateDrawHealthBar(NPC npc, byte bhPosition, ref float scale, ref Vector2 position);
706  private static HookList HookDrawHealthBar = AddHook<DelegateDrawHealthBar>(g => g.DrawHealthBar);
707 
708  public static bool DrawHealthBar(NPC npc, ref float scale) {
709  Vector2 position = new Vector2(npc.position.X + npc.width / 2, npc.position.Y + npc.gfxOffY);
710  if (Main.HealthBarDrawSettings == 1) {
711  position.Y += npc.height + 10f + Main.NPCAddHeight(npc.whoAmI);
712  }
713  else if (Main.HealthBarDrawSettings == 2) {
714  position.Y -= 24f + Main.NPCAddHeight(npc.whoAmI) / 2f;
715  }
716  foreach (GlobalNPC g in HookDrawHealthBar.arr) {
717  bool? result = g.Instance(npc).DrawHealthBar(npc, Main.HealthBarDrawSettings, ref scale, ref position);
718  if (result.HasValue) {
719  if (result.Value) {
720  DrawHealthBar(npc, position, scale);
721  }
722  return false;
723  }
724  }
725  if (NPCLoader.IsModNPC(npc)) {
726  bool? result = npc.modNPC.DrawHealthBar(Main.HealthBarDrawSettings, ref scale, ref position);
727  if (result.HasValue) {
728  if (result.Value) {
729  DrawHealthBar(npc, position, scale);
730  }
731  return false;
732  }
733  }
734  return true;
735  }
736 
737  private static void DrawHealthBar(NPC npc, Vector2 position, float scale) {
738  float alpha = Lighting.Brightness((int)(npc.Center.X / 16f), (int)(npc.Center.Y / 16f));
739  Main.instance.DrawHealthBar(position.X, position.Y, npc.life, npc.lifeMax, alpha, scale);
740  }
741 
742  private delegate void DelegateEditSpawnRate(Player player, ref int spawnRate, ref int maxSpawns);
743  private static HookList HookEditSpawnRate = AddHook<DelegateEditSpawnRate>(g => g.EditSpawnRate);
744 
745  public static void EditSpawnRate(Player player, ref int spawnRate, ref int maxSpawns) {
746  foreach (GlobalNPC g in HookEditSpawnRate.arr) {
747  g.EditSpawnRate(player, ref spawnRate, ref maxSpawns);
748  }
749  }
750 
751  private delegate void DelegateEditSpawnRange(Player player, ref int spawnRangeX, ref int spawnRangeY,
752  ref int safeRangeX, ref int safeRangeY);
753  private static HookList HookEditSpawnRange = AddHook<DelegateEditSpawnRange>(g => g.EditSpawnRange);
754 
755  public static void EditSpawnRange(Player player, ref int spawnRangeX, ref int spawnRangeY,
756  ref int safeRangeX, ref int safeRangeY) {
757  foreach (GlobalNPC g in HookEditSpawnRange.arr) {
758  g.EditSpawnRange(player, ref spawnRangeX, ref spawnRangeY, ref safeRangeX, ref safeRangeY);
759  }
760  }
761 
762  private static HookList HookEditSpawnPool = AddHook<Action<Dictionary<int, float>, NPCSpawnInfo>>(g => g.EditSpawnPool);
763 
764  public static int? ChooseSpawn(NPCSpawnInfo spawnInfo) {
765  NPCSpawnHelper.Reset();
766  NPCSpawnHelper.DoChecks(spawnInfo);
767  IDictionary<int, float> pool = new Dictionary<int, float>();
768  pool[0] = 1f;
769  foreach (ModNPC npc in npcs) {
770  float weight = npc.SpawnChance(spawnInfo);
771  if (weight > 0f) {
772  pool[npc.npc.type] = weight;
773  }
774  }
775  foreach (GlobalNPC g in HookEditSpawnPool.arr) {
776  g.EditSpawnPool(pool, spawnInfo);
777  }
778  float totalWeight = 0f;
779  foreach (int type in pool.Keys) {
780  if (pool[type] < 0f) {
781  pool[type] = 0f;
782  }
783  totalWeight += pool[type];
784  }
785  float choice = (float)Main.rand.NextDouble() * totalWeight;
786  foreach (int type in pool.Keys) {
787  float weight = pool[type];
788  if (choice < weight) {
789  return type;
790  }
791  choice -= weight;
792  }
793  return null;
794  }
795 
796  private static HookList HookSpawnNPC = AddHook<Action<int, int, int>>(g => g.SpawnNPC);
797 
798  public static int SpawnNPC(int type, int tileX, int tileY) {
799  var npc = type >= NPCID.Count ?
800  GetNPC(type).SpawnNPC(tileX, tileY) :
801  NPC.NewNPC(tileX * 16 + 8, tileY * 16, type);
802 
803  foreach (GlobalNPC g in HookSpawnNPC.arr) {
804  g.Instance(Main.npc[npc]).SpawnNPC(npc, tileX, tileY);
805  }
806 
807  return npc;
808  }
809 
810  public static void CanTownNPCSpawn(int numTownNPCs, int money) {
811  foreach (ModNPC npc in npcs) {
812  if (npc.npc.townNPC && NPC.TypeToHeadIndex(npc.npc.type) >= 0 && !NPC.AnyNPCs(npc.npc.type) &&
813  npc.CanTownNPCSpawn(numTownNPCs, money)) {
814  Main.townNPCCanSpawn[npc.npc.type] = true;
815  if (WorldGen.prioritizedTownNPC == 0) {
816  WorldGen.prioritizedTownNPC = npc.npc.type;
817  }
818  }
819  }
820  }
821 
822  public static bool CheckConditions(int type) {
823  return GetNPC(type)?.CheckConditions(WorldGen.roomX1, WorldGen.roomX2, WorldGen.roomY1, WorldGen.roomY2) ?? true;
824  }
825 
826  public static string TownNPCName(int type) {
827  return GetNPC(type)?.TownNPCName() ?? "";
828  }
829 
830  public static bool UsesPartyHat(NPC npc) {
831  return npc.modNPC?.UsesPartyHat() ?? true;
832  }
833 
834  public static bool VanillaCanChat(NPC npc) {
835  switch (npc.type) {
836  case NPCID.BoundGoblin:
837  case NPCID.BoundWizard:
838  case NPCID.BoundMechanic:
839  case NPCID.WebbedStylist:
840  case NPCID.SleepingAngler:
841  case NPCID.BartenderUnconscious:
842  case NPCID.SkeletonMerchant:
843  return true;
844  default:
845  return npc.townNPC;
846  }
847  }
848 
849  private static HookList HookCanChat = AddHook<Func<NPC, bool?>>(g => g.CanChat);
850 
851  public static bool CanChat(NPC npc) {
852  bool defaultCanChat = npc.modNPC?.CanChat() ?? VanillaCanChat(npc);
853 
854  foreach (GlobalNPC g in HookCanChat.arr) {
855  bool? canChat = g.Instance(npc).CanChat(npc);
856  if (canChat.HasValue) {
857  if (!canChat.Value) {
858  return false;
859  }
860  defaultCanChat = true;
861  }
862  }
863 
864  return defaultCanChat;
865  }
866 
867  private delegate void DelegateGetChat(NPC npc, ref string chat);
868  private static HookList HookGetChat = AddHook<DelegateGetChat>(g => g.GetChat);
869 
870  public static void GetChat(NPC npc, ref string chat) {
871  if (npc.modNPC != null) {
872  chat = npc.modNPC.GetChat();
873  }
874  else if (chat.Equals("")) {
875  chat = Language.GetTextValue("tModLoader.DefaultTownNPCChat");
876  }
877  foreach (GlobalNPC g in HookGetChat.arr) {
878  g.Instance(npc).GetChat(npc, ref chat);
879  }
880  }
881 
882  public static void SetChatButtons(ref string button, ref string button2) {
883  if (Main.player[Main.myPlayer].talkNPC >= 0) {
884  NPC npc = Main.npc[Main.player[Main.myPlayer].talkNPC];
885  npc.modNPC?.SetChatButtons(ref button, ref button2);
886  }
887  }
888 
889  private static HookList HookPreChatButtonClicked = AddHook<Func<NPC, bool, bool>>(g => g.PreChatButtonClicked);
890 
891  public static bool PreChatButtonClicked(bool firstButton) {
892  NPC npc = Main.npc[Main.LocalPlayer.talkNPC];
893 
894  foreach (GlobalNPC g in HookPreChatButtonClicked.arr) {
895  if (!g.Instance(npc).PreChatButtonClicked(npc, firstButton)) {
896  Main.PlaySound(SoundID.MenuTick);
897  return false;
898  }
899  }
900  return true;
901  }
902 
903  private delegate void DelegateOnChatButtonClicked(NPC npc, bool firstButton);
904  private static HookList HookOnChatButtonClicked = AddHook<DelegateOnChatButtonClicked>(g => g.OnChatButtonClicked);
905 
906  public static void OnChatButtonClicked(bool firstButton) {
907  NPC npc = Main.npc[Main.LocalPlayer.talkNPC];
908  bool shop = false;
909 
910  if (npc.modNPC != null) {
911  npc.modNPC.OnChatButtonClicked(firstButton, ref shop);
912  Main.PlaySound(SoundID.MenuTick);
913  if (shop) {
914  Main.playerInventory = true;
915  Main.npcChatText = "";
916  Main.npcShop = Main.MaxShopIDs - 1;
917  Main.instance.shop[Main.npcShop].SetupShop(npc.type);
918  }
919  }
920  foreach (GlobalNPC g in HookOnChatButtonClicked.arr) {
921  g.Instance(npc).OnChatButtonClicked(npc, firstButton);
922  }
923  }
924 
925  private delegate void DelegateSetupShop(int type, Chest shop, ref int nextSlot);
926  private static HookList HookSetupShop = AddHook<DelegateSetupShop>(g => g.SetupShop);
927 
928  public static void SetupShop(int type, Chest shop, ref int nextSlot) {
929  if (type < shopToNPC.Length) {
930  type = shopToNPC[type];
931  }
932  else {
933  GetNPC(type)?.SetupShop(shop, ref nextSlot);
934  }
935  foreach (GlobalNPC g in HookSetupShop.arr) {
936  g.SetupShop(type, shop, ref nextSlot);
937  }
938  }
939 
940  private delegate void DelegateSetupTravelShop(int[] shop, ref int nextSlot);
941  private static HookList HookSetupTravelShop = AddHook<DelegateSetupTravelShop>(g => g.SetupTravelShop);
942 
943  public static void SetupTravelShop(int[] shop, ref int nextSlot) {
944  foreach (GlobalNPC g in HookSetupTravelShop.arr) {
945  g.SetupTravelShop(shop, ref nextSlot);
946  }
947  }
948 
949  private static HookList HookCanGoToStatue = AddHook<Func<NPC, bool, bool?>>(g => g.CanGoToStatue);
950 
951  public static bool CanGoToStatue(NPC npc, bool toKingStatue, bool vanillaCanGo) {
952  bool defaultCanGo = npc.modNPC?.CanGoToStatue(toKingStatue) ?? vanillaCanGo;
953 
954  foreach (GlobalNPC g in HookCanGoToStatue.arr) {
955  bool? canGo = g.Instance(npc).CanGoToStatue(npc, toKingStatue);
956  if (canGo.HasValue) {
957  if (!canGo.Value) {
958  return false;
959  }
960  defaultCanGo = true;
961  }
962  }
963 
964  return defaultCanGo;
965  }
966 
967  private static HookList HookOnGoToStatue = AddHook<Action<NPC, bool>>(g => g.OnGoToStatue);
968 
969  public static void OnGoToStatue(NPC npc, bool toKingStatue) {
970  npc.modNPC?.OnGoToStatue(toKingStatue);
971 
972  foreach (GlobalNPC g in HookOnGoToStatue.arr) {
973  g.OnGoToStatue(npc, toKingStatue);
974  }
975  }
976 
977  private delegate void DelegateBuffTownNPC(ref float damageMult, ref int defense);
978  private static HookList HookBuffTownNPC = AddHook<DelegateBuffTownNPC>(g => g.BuffTownNPC);
979 
980  public static void BuffTownNPC(ref float damageMult, ref int defense) {
981  foreach (GlobalNPC g in HookBuffTownNPC.arr) {
982  g.BuffTownNPC(ref damageMult, ref defense);
983  }
984  }
985  //attack type 0 = throwing
986  // num405 = type, num406 = damage, knockBack, scaleFactor7 = speed multiplier, num407 = attack delay
987  // num408 = unknown, maxValue3 = unknown, num409 = gravity correction factor, num411 = random speed offset
988  //attack type 1 = shooting
989  // num413 = type, num414 = damage, scaleFactor8 = speed multiplier, num415 = attack delay,
990  // num416 = unknown, maxValue4 = unknown, knockBack2, num417 = gravity correction,
991  // flag53 = in between shots, num418 = random speed offset
992  //attack type 2 = magic
993  // num423 = type, num424 = damage, scaleFactor9 = speed multiplier, num425 = attack delay,
994  // num426 = unknown, maxValue5 = unknown, knockBack3, num427 = gravity correction factor,
995  // num429 = aura light multiplier, num430 = random speed offset
996  //attack type 3 = swinging
997  // num439 = unknown, maxValue6 = unknown, num440 = damage, num441 = knockback,
998  // num442 = item width, num443 = item height
999  //unknowns are associated with ai[1], localAI[1], and localAI[3] when ai[0] is either 0 or 8
1000  //check patch files for town NPC attacks
1001  private delegate void DelegateTownNPCAttackStrength(NPC npc, ref int damage, ref float knockback);
1002  private static HookList HookTownNPCAttackStrength = AddHook<DelegateTownNPCAttackStrength>(g => g.TownNPCAttackStrength);
1003 
1004  public static void TownNPCAttackStrength(NPC npc, ref int damage, ref float knockback) {
1005  npc.modNPC?.TownNPCAttackStrength(ref damage, ref knockback);
1006 
1007  foreach (GlobalNPC g in HookTownNPCAttackStrength.arr) {
1008  g.Instance(npc).TownNPCAttackStrength(npc, ref damage, ref knockback);
1009  }
1010  }
1011 
1012  private delegate void DelegateTownNPCAttackCooldown(NPC npc, ref int cooldown, ref int randExtraCooldown);
1013  private static HookList HookTownNPCAttackCooldown = AddHook<DelegateTownNPCAttackCooldown>(g => g.TownNPCAttackCooldown);
1014 
1015  public static void TownNPCAttackCooldown(NPC npc, ref int cooldown, ref int randExtraCooldown) {
1016  npc.modNPC?.TownNPCAttackCooldown(ref cooldown, ref randExtraCooldown);
1017 
1018  foreach (GlobalNPC g in HookTownNPCAttackCooldown.arr) {
1019  g.Instance(npc).TownNPCAttackCooldown(npc, ref cooldown, ref randExtraCooldown);
1020  }
1021  }
1022 
1023  private delegate void DelegateTownNPCAttackProj(NPC npc, ref int projType, ref int attackDelay);
1024  private static HookList HookTownNPCAttackProj = AddHook<DelegateTownNPCAttackProj>(g => g.TownNPCAttackProj);
1025 
1026  public static void TownNPCAttackProj(NPC npc, ref int projType, ref int attackDelay) {
1027  npc.modNPC?.TownNPCAttackProj(ref projType, ref attackDelay);
1028 
1029  foreach (GlobalNPC g in HookTownNPCAttackProj.arr) {
1030  g.Instance(npc).TownNPCAttackProj(npc, ref projType, ref attackDelay);
1031  }
1032  }
1033 
1034  private delegate void DelegateTownNPCAttackProjSpeed(NPC npc, ref float multiplier, ref float gravityCorrection,
1035  ref float randomOffset);
1036  private static HookList HookTownNPCAttackProjSpeed = AddHook<DelegateTownNPCAttackProjSpeed>(g => g.TownNPCAttackProjSpeed);
1037 
1038  public static void TownNPCAttackProjSpeed(NPC npc, ref float multiplier, ref float gravityCorrection,
1039  ref float randomOffset) {
1040  npc.modNPC?.TownNPCAttackProjSpeed(ref multiplier, ref gravityCorrection, ref randomOffset);
1041 
1042  foreach (GlobalNPC g in HookTownNPCAttackProjSpeed.arr) {
1043  g.Instance(npc).TownNPCAttackProjSpeed(npc, ref multiplier, ref gravityCorrection, ref randomOffset);
1044  }
1045  }
1046 
1047  private delegate void DelegateTownNPCAttackShoot(NPC npc, ref bool inBetweenShots);
1048  private static HookList HookTownNPCAttackShoot = AddHook<DelegateTownNPCAttackShoot>(g => g.TownNPCAttackShoot);
1049 
1050  public static void TownNPCAttackShoot(NPC npc, ref bool inBetweenShots) {
1051  npc.modNPC?.TownNPCAttackShoot(ref inBetweenShots);
1052 
1053  foreach (GlobalNPC g in HookTownNPCAttackShoot.arr) {
1054  g.Instance(npc).TownNPCAttackShoot(npc, ref inBetweenShots);
1055  }
1056  }
1057 
1058  private delegate void DelegateTownNPCAttackMagic(NPC npc, ref float auraLightMultiplier);
1059  private static HookList HookTownNPCAttackMagic = AddHook<DelegateTownNPCAttackMagic>(g => g.TownNPCAttackMagic);
1060 
1061  public static void TownNPCAttackMagic(NPC npc, ref float auraLightMultiplier) {
1062  npc.modNPC?.TownNPCAttackMagic(ref auraLightMultiplier);
1063 
1064  foreach (GlobalNPC g in HookTownNPCAttackMagic.arr) {
1065  g.Instance(npc).TownNPCAttackMagic(npc, ref auraLightMultiplier);
1066  }
1067  }
1068 
1069  private delegate void DelegateTownNPCAttackSwing(NPC npc, ref int itemWidth, ref int itemHeight);
1070  private static HookList HookTownNPCAttackSwing = AddHook<DelegateTownNPCAttackSwing>(g => g.TownNPCAttackSwing);
1071 
1072  public static void TownNPCAttackSwing(NPC npc, ref int itemWidth, ref int itemHeight) {
1073  npc.modNPC?.TownNPCAttackSwing(ref itemWidth, ref itemHeight);
1074 
1075  foreach (GlobalNPC g in HookTownNPCAttackSwing.arr) {
1076  g.Instance(npc).TownNPCAttackSwing(npc, ref itemWidth, ref itemHeight);
1077  }
1078  }
1079 
1080  private delegate void DelegateDrawTownAttackGun(NPC npc, ref float scale, ref int item, ref int closeness);
1081  private static HookList HookDrawTownAttackGun = AddHook<DelegateDrawTownAttackGun>(g => g.DrawTownAttackGun);
1082 
1083  public static void DrawTownAttackGun(NPC npc, ref float scale, ref int item, ref int closeness) {
1084  npc.modNPC?.DrawTownAttackGun(ref scale, ref item, ref closeness);
1085 
1086  foreach (GlobalNPC g in HookDrawTownAttackGun.arr) {
1087  g.Instance(npc).DrawTownAttackGun(npc, ref scale, ref item, ref closeness);
1088  }
1089  }
1090 
1091  private delegate void DelegateDrawTownAttackSwing(NPC npc, ref Texture2D item, ref int itemSize, ref float scale, ref Vector2 offset);
1092  private static HookList HookDrawTownAttackSwing = AddHook<DelegateDrawTownAttackSwing>(g => g.DrawTownAttackSwing);
1093 
1094  public static void DrawTownAttackSwing(NPC npc, ref Texture2D item, ref int itemSize, ref float scale, ref Vector2 offset) {
1095  npc.modNPC?.DrawTownAttackSwing(ref item, ref itemSize, ref scale, ref offset);
1096 
1097  foreach (GlobalNPC g in HookDrawTownAttackSwing.arr) {
1098  g.Instance(npc).DrawTownAttackSwing(npc, ref item, ref itemSize, ref scale, ref offset);
1099  }
1100  }
1101 
1102  private static bool HasMethod(Type t, string method, params Type[] args) {
1103  return t.GetMethod(method, args).DeclaringType != typeof(GlobalNPC);
1104  }
1105 
1106  internal static void VerifyGlobalNPC(GlobalNPC npc) {
1107  var type = npc.GetType();
1108 
1109  bool hasInstanceFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
1110  .Any(f => f.DeclaringType != typeof(GlobalNPC));
1111  if (hasInstanceFields) {
1112  if (!npc.InstancePerEntity) {
1113  throw new Exception(type + " has instance fields but does not set InstancePerEntity to true. Either use static fields, or per instance globals");
1114  }
1115  }
1116  }
1117  }
1118 }
static void BossLoot(NPC npc, ref string name, ref int potionType)
Definition: NPCLoader.cs:426
static void NPCLoot(NPC npc)
Definition: NPCLoader.cs:417
static void BossHeadSpriteEffects(NPC npc, ref SpriteEffects spriteEffects)
Definition: NPCLoader.cs:650
static void PostDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor)
Definition: NPCLoader.cs:697
static bool CheckDead(NPC npc)
Definition: NPCLoader.cs:373
static void TownNPCAttackProj(NPC npc, ref int projType, ref int attackDelay)
Definition: NPCLoader.cs:1026
virtual bool CanHitPlayer(NPC npc, Player target, ref int cooldownSlot)
Allows you to determine whether an NPC can hit the given player. Return false to block the NPC from h...
Definition: GlobalNPC.cs:201
static void NPCAI(NPC npc)
Definition: NPCLoader.cs:239
virtual bool PreNPCLoot(NPC npc)
Allows you to determine whether or not the NPC will drop anything at all. Return false to stop the NP...
Definition: GlobalNPC.cs:183
static void DrawTownAttackSwing(NPC npc, ref Texture2D item, ref int itemSize, ref float scale, ref Vector2 offset)
Definition: NPCLoader.cs:1094
This server as a central class to help modders spawn their npcs. It&#39;s basically the vanilla spawn cod...
static void AI(NPC npc)
Definition: NPCLoader.cs:271
static void BuffTownNPC(ref float damageMult, ref int defense)
Definition: NPCLoader.cs:980
This class serves as a place for you to place all your properties and hooks for each NPC...
Definition: ModNPC.cs:14
static int ChooseSpawn(NPCSpawnInfo spawnInfo)
Definition: NPCLoader.cs:764
virtual void OnHitByProjectile(NPC npc, Projectile projectile, int damage, float knockback, bool crit)
Allows you to create special effects when an NPC is hit by a projectile.
Definition: GlobalNPC.cs:322
virtual bool CheckDead(NPC npc)
Whether or not an NPC should be killed when it reaches 0 health. You may program extra effects in thi...
Definition: GlobalNPC.cs:166
static void TownNPCAttackShoot(NPC npc, ref bool inBetweenShots)
Definition: NPCLoader.cs:1050
static bool AllowVanillaClients
Definition: ModNet.cs:52
virtual void DrawTownAttackSwing(NPC npc, ref Texture2D item, ref int itemSize, ref float scale, ref Vector2 offset)
Allows you to customize how a town NPC&#39;s weapon is drawn when the NPC is swinging it (the NPC must ha...
Definition: GlobalNPC.cs:610
virtual void ModifyHitNPC(NPC npc, NPC target, ref int damage, ref float knockback, ref bool crit)
Allows you to modify the damage, knockback, etc., that an NPC does to a friendly NPC.
Definition: GlobalNPC.cs:243
static void BossHeadRotation(NPC npc, ref float rotation)
Definition: NPCLoader.cs:639
static void OnHitByItem(NPC npc, Player player, Item item, int damage, float knockback, bool crit)
Definition: NPCLoader.cs:555
static bool DrawHealthBar(NPC npc, ref float scale)
Definition: NPCLoader.cs:708
static void UpdateLifeRegen(NPC npc, ref int damage)
Definition: NPCLoader.cs:349
virtual bool CanChat(NPC npc)
Allows you to determine whether this NPC can talk with the player. Return true to allow talking with ...
Definition: GlobalNPC.cs:456
virtual void SetupShop(int type, Chest shop, ref int nextSlot)
Allows you to add items to an NPC&#39;s shop. The type parameter is the type of the NPC that this shop be...
Definition: GlobalNPC.cs:492
virtual float SpawnChance(NPCSpawnInfo spawnInfo)
Whether or not this NPC can spawn with the given spawning conditions. Return the weight for the chanc...
Definition: ModNPC.cs:539
static bool CanChat(NPC npc)
Definition: NPCLoader.cs:851
static void BossBag(NPC npc, ref int bagType)
Definition: NPCLoader.cs:430
A struct that stores information regarding where an NPC is naturally spawning and the player it is sp...
Definition: NPCSpawnInfo.cs:6
static bool PreDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor)
Definition: NPCLoader.cs:683
virtual void TownNPCAttackStrength(NPC npc, ref int damage, ref float knockback)
Allows you to determine the damage and knockback of a town NPC&#39;s attack before the damage is scaled...
Definition: GlobalNPC.cs:535
static void PostAI(NPC npc)
Definition: NPCLoader.cs:281
static void HitEffect(NPC npc, int hitDirection, double damage)
Definition: NPCLoader.cs:337
static bool StrikeNPC(NPC npc, ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit)
Definition: NPCLoader.cs:612
static void ScaleExpertStats(NPC npc, int numPlayers, float bossLifeScale)
Definition: NPCLoader.cs:221
static void ReceiveExtraAI(NPC npc, BinaryReader reader)
Definition: NPCLoader.cs:306
virtual void SpawnNPC(int npc, int tileX, int tileY)
Allows you to customize an NPC (for example, its position or ai array) after it naturally spawns and ...
Definition: GlobalNPC.cs:448
virtual void ResetEffects(NPC npc)
This is where you reset any fields you add to your subclass to their default states. This is necessary in order to reset your fields if they are conditionally set by a tick update but the condition is no longer satisfied.
Definition: GlobalNPC.cs:101
static void TownNPCAttackSwing(NPC npc, ref int itemWidth, ref int itemHeight)
Definition: NPCLoader.cs:1072
virtual void EditSpawnRange(Player player, ref int spawnRangeX, ref int spawnRangeY, ref int safeRangeX, ref int safeRangeY)
Allows you to modify the range at which NPCs can spawn around the given player. The spawnRanges deter...
Definition: GlobalNPC.cs:430
virtual void SetupTravelShop(int[] shop, ref int nextSlot)
Allows you to add items to the traveling merchant&#39;s shop. Add an item by setting shop[nextSlot] to th...
Definition: GlobalNPC.cs:500
static bool CanHitNPC(NPC npc, NPC target)
Definition: NPCLoader.cs:474
virtual void GetChat(NPC npc, ref string chat)
Allows you to modify the chat message of any NPC that the player can talk to.
Definition: GlobalNPC.cs:465
virtual void ScaleExpertStats(NPC npc, int numPlayers, float bossLifeScale)
Allows you to customize an NPC&#39;s stats in expert mode.
Definition: GlobalNPC.cs:94
virtual void UpdateLifeRegen(NPC npc, ref int damage)
Allows you to make the NPC either regenerate health or take damage over time by setting npc...
Definition: GlobalNPC.cs:149
virtual void BossHeadRotation(NPC npc, ref float rotation)
Allows you to customize the rotation of an NPC&#39;s boss head icon on the map.
Definition: GlobalNPC.cs:352
This serves as the central class which loads mods. It contains many static fields and methods related...
Definition: ModLoader.cs:24
virtual void OnHitNPC(NPC npc, NPC target, int damage, float knockback, bool crit)
Allows you to create special effects when an NPC hits a friendly NPC.
Definition: GlobalNPC.cs:254
virtual void ModifyHitByProjectile(NPC npc, Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
Allows you to modify the damage, knockback, etc., that an NPC takes from a projectile.
Definition: GlobalNPC.cs:311
static bool CanBeHitByItem(NPC npc, Player player, Item item)
Definition: NPCLoader.cs:519
virtual void OnHitByItem(NPC npc, Player player, Item item, int damage, float knockback, bool crit)
Allows you to create special effects when an NPC is hit by a melee weapon.
Definition: GlobalNPC.cs:289
virtual bool InstancePerEntity
Whether to create a new GlobalNPC instance for every NPC that exists. Useful for storing information ...
Definition: GlobalNPC.cs:46
static void ModifyHitByProjectile(NPC npc, Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
Definition: NPCLoader.cs:591
virtual void AI(NPC npc)
Allows you to determine how any NPC behaves. This will only be called if PreAI returns true...
Definition: GlobalNPC.cs:117
static void ModifyHitNPC(NPC npc, NPC target, ref int damage, ref float knockback, ref bool crit)
Definition: NPCLoader.cs:500
virtual int SpawnNPC(int tileX, int tileY)
Allows you to customize how this NPC is created when it naturally spawns (for example, its position or ai array). Return the return value of NPC.NewNPC. By default this method spawns this NPC on top of the tile at the given coordinates.
Definition: ModNPC.cs:549
virtual void ModifyHitByItem(NPC npc, Player player, Item item, ref int damage, ref float knockback, ref bool crit)
Allows you to modify the damage, knockback, etc., that an NPC takes from a melee weapon.
Definition: GlobalNPC.cs:277
virtual void TownNPCAttackProjSpeed(NPC npc, ref float multiplier, ref float gravityCorrection, ref float randomOffset)
Allows you to determine the speed at which a town NPC throws a projectile when it attacks...
Definition: GlobalNPC.cs:563
NPC npc
The NPC object that this ModNPC controls.
Definition: ModNPC.cs:21
static bool PreNPCLoot(NPC npc)
Definition: NPCLoader.cs:401
static void TownNPCAttackCooldown(NPC npc, ref int cooldown, ref int randExtraCooldown)
Definition: NPCLoader.cs:1015
static void TownNPCAttackProjSpeed(NPC npc, ref float multiplier, ref float gravityCorrection, ref float randomOffset)
Definition: NPCLoader.cs:1038
virtual void FindFrame(NPC npc, int frameHeight)
Allows you to modify the frame from an NPC&#39;s texture that is drawn, which is necessary in order to an...
Definition: GlobalNPC.cs:132
static int SpawnNPC(int type, int tileX, int tileY)
Definition: NPCLoader.cs:798
virtual bool CanGoToStatue(NPC npc, bool toKingStatue)
Whether this NPC can be telported a King or Queen statue. Return true to allow the NPC to teleport to...
Definition: GlobalNPC.cs:508
virtual void OnHitPlayer(NPC npc, Player target, int damage, bool crit)
Allows you to create special effects when an NPC hits a player (for example, inflicting debuffs)...
Definition: GlobalNPC.cs:222
static void SetupShop(int type, Chest shop, ref int nextSlot)
Definition: NPCLoader.cs:928
virtual void PostAI(NPC npc)
Allows you to determine how any NPC behaves. This will be called regardless of what PreAI returns...
Definition: GlobalNPC.cs:124
virtual void DrawEffects(NPC npc, ref Color drawColor)
Allows you to add special visual effects to an NPC (such as creating dust), and modify the color in w...
Definition: GlobalNPC.cs:378
static void DrawTownAttackGun(NPC npc, ref float scale, ref int item, ref int closeness)
Definition: NPCLoader.cs:1083
virtual bool CanBeHitByItem(NPC npc, Player player, Item item)
Allows you to determine whether an NPC can be hit by the given melee weapon when swung. Return true to allow hitting the NPC, return false to block hitting the NPC, and return null to use the vanilla code for whether the NPC can be hit. Returns null by default.
Definition: GlobalNPC.cs:264
virtual bool CanBeHitByProjectile(NPC npc, Projectile projectile)
Allows you to determine whether an NPC can be hit by the given projectile. Return true to allow hitti...
Definition: GlobalNPC.cs:298
static bool PreAI(NPC npc)
Definition: NPCLoader.cs:257
static void CanTownNPCSpawn(int numTownNPCs, int money)
Definition: NPCLoader.cs:810
virtual void SetDefaults(NPC npc)
Allows you to set the properties of any and every NPC that gets created.
Definition: GlobalNPC.cs:85
static bool CanGoToStatue(NPC npc, bool toKingStatue, bool vanillaCanGo)
Definition: NPCLoader.cs:951
static void DrawEffects(NPC npc, ref Color drawColor)
Definition: NPCLoader.cs:673
virtual bool PreAI(NPC npc)
Allows you to determine how any NPC behaves. Return false to stop the vanilla AI and the AI hook from...
Definition: GlobalNPC.cs:109
static void SendExtraAI(NPC npc, BinaryWriter writer)
Definition: NPCLoader.cs:289
virtual void BossHeadSlot(NPC npc, ref int index)
Allows you to customize the boss head texture used by an NPC based on its state.
Definition: GlobalNPC.cs:344
virtual bool CheckActive(NPC npc)
Whether or not to run the code for checking whether an NPC will remain active. Return false to stop t...
Definition: GlobalNPC.cs:157
static void OnChatButtonClicked(bool firstButton)
Definition: NPCLoader.cs:906
This class allows you to modify and use hooks for all NPCs, including vanilla mobs. Create an instance of an overriding class then call Mod.AddGlobalNPC to use this.
Definition: GlobalNPC.cs:11
static bool VanillaCanChat(NPC npc)
Definition: NPCLoader.cs:834
static bool CheckActive(NPC npc)
Definition: NPCLoader.cs:359
virtual bool StrikeNPC(NPC npc, ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit)
Allows you to use a custom damage formula for when an NPC takes damage from any source. For example, you can change the way defense works or use a different crit multiplier. Return false to stop the game from running the vanilla damage formula; returns true by default.
Definition: GlobalNPC.cs:335
virtual void TownNPCAttackShoot(NPC npc, ref bool inBetweenShots)
Allows you to tell the game that a town NPC has already created a projectile and will still create mo...
Definition: GlobalNPC.cs:572
static void EditSpawnRate(Player player, ref int spawnRate, ref int maxSpawns)
Definition: NPCLoader.cs:745
static bool HasMethod(Type t, string method, params Type[] args)
Definition: NPCLoader.cs:1102
static void OnHitByProjectile(NPC npc, Projectile projectile, int damage, float knockback, bool crit)
Definition: NPCLoader.cs:601
static bool CheckConditions(int type)
Definition: NPCLoader.cs:822
static void BossHeadSlot(NPC npc, ref int index)
Definition: NPCLoader.cs:628
static ModNPC GetNPC(int type)
Gets the ModNPC instance corresponding to the specified type.
Definition: NPCLoader.cs:93
static void TownNPCAttackMagic(NPC npc, ref float auraLightMultiplier)
Definition: NPCLoader.cs:1061
virtual void HitEffect(NPC npc, int hitDirection, double damage)
Allows you to make things happen whenever an NPC is hit, such as creating dust or gores...
Definition: GlobalNPC.cs:141
static string TownNPCName(int type)
Definition: NPCLoader.cs:826
GlobalNPC Instance(NPC npc)
virtual bool DrawHealthBar(NPC npc, byte hbPosition, ref float scale, ref Vector2 position)
Allows you to control how the health bar for the given NPC is drawn. The hbPosition parameter is the ...
Definition: GlobalNPC.cs:409
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
This serves as the central class from which NPC-related functions are carried out. It also stores a list of mod NPCs by ID.
Definition: NPCLoader.cs:19
static void FindFrame(NPC npc, int frameHeight)
Definition: NPCLoader.cs:321
static void OnHitNPC(NPC npc, NPC target, int damage, float knockback, bool crit)
Definition: NPCLoader.cs:510
static bool UsesPartyHat(NPC npc)
Definition: NPCLoader.cs:830
static void GetChat(NPC npc, ref string chat)
Definition: NPCLoader.cs:870
virtual void TownNPCAttackProj(NPC npc, ref int projType, ref int attackDelay)
Allows you to determine the projectile type of a town NPC&#39;s attack, and how long it takes for the pro...
Definition: GlobalNPC.cs:553
This serves as the central class from which buff-related functions are supported and carried out...
Definition: BuffLoader.cs:13
virtual void ModifyHitPlayer(NPC npc, Player target, ref int damage, ref bool crit)
Allows you to modify the damage, etc., that an NPC does to a player.
Definition: GlobalNPC.cs:212
static void OnGoToStatue(NPC npc, bool toKingStatue)
Definition: NPCLoader.cs:969
virtual bool CanTownNPCSpawn(int numTownNPCs, int money)
Whether or not the conditions have been met for this town NPC to be able to move into town...
Definition: ModNPC.cs:559
static bool CanBeHitByProjectile(NPC npc, Projectile projectile)
Definition: NPCLoader.cs:565
virtual void EditSpawnRate(Player player, ref int spawnRate, ref int maxSpawns)
Allows you to modify the chance of NPCs spawning around the given player and the maximum number of NP...
Definition: GlobalNPC.cs:419
static Color GetAlpha(NPC npc, Color lightColor)
Definition: NPCLoader.cs:660
static bool SpecialNPCLoot(NPC npc)
Definition: NPCLoader.cs:387
virtual void EditSpawnPool(IDictionary< int, float > pool, NPCSpawnInfo spawnInfo)
Allows you to control which NPCs can spawn and how likely each one is to spawn. The pool parameter ma...
Definition: GlobalNPC.cs:439
virtual bool PreDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor)
Allows you to draw things behind an NPC, or to modify the way the NPC is drawn. Return false to stop ...
Definition: GlobalNPC.cs:388
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 bool SpecialNPCLoot(NPC npc)
Allows you to call NPCLoot on your own when the NPC dies, rather then letting vanilla call it on its ...
Definition: GlobalNPC.cs:174
static void ModifyHitByItem(NPC npc, Player player, Item item, ref int damage, ref float knockback, ref bool crit)
Definition: NPCLoader.cs:545
static void SetupTravelShop(int[] shop, ref int nextSlot)
Definition: NPCLoader.cs:943
virtual bool PreChatButtonClicked(NPC npc, bool firstButton)
Allows you to determine if something can happen whenever a button is clicked on this NPC&#39;s chat windo...
Definition: GlobalNPC.cs:474
virtual void DrawTownAttackGun(NPC npc, ref float scale, ref int item, ref int closeness)
Allows you to customize how a town NPC&#39;s weapon is drawn when the NPC is shooting (the NPC must have ...
Definition: GlobalNPC.cs:599
static bool PreChatButtonClicked(bool firstButton)
Definition: NPCLoader.cs:891
virtual void NPCLoot(NPC npc)
Allows you to add drops to an NPC when it dies.
Definition: GlobalNPC.cs:191
virtual Color GetAlpha(NPC npc, Color drawColor)
Allows you to determine the color and transparency in which an NPC is drawn. Return null to use the d...
Definition: GlobalNPC.cs:369
virtual void TownNPCAttackMagic(NPC npc, ref float auraLightMultiplier)
Allows you to control the brightness of the light emitted by a town NPC&#39;s aura when it performs a mag...
Definition: GlobalNPC.cs:580
static void SetChatButtons(ref string button, ref string button2)
Definition: NPCLoader.cs:882
virtual bool CanHitNPC(NPC npc, NPC target)
Allows you to determine whether an NPC can hit the given friendly NPC. Return true to allow hitting t...
Definition: GlobalNPC.cs:231
virtual void PostDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor)
Allows you to draw things in front of this NPC. This method is called even if PreDraw returns false...
Definition: GlobalNPC.cs:398
virtual void OnChatButtonClicked(NPC npc, bool firstButton)
Allows you to make something happen whenever a button is clicked on this NPC&#39;s chat window...
Definition: GlobalNPC.cs:483
virtual void TownNPCAttackSwing(NPC npc, ref int itemWidth, ref int itemHeight)
Allows you to determine the width and height of the item a town NPC swings when it attacks...
Definition: GlobalNPC.cs:589
virtual void BossHeadSpriteEffects(NPC npc, ref SpriteEffects spriteEffects)
Allows you to flip an NPC&#39;s boss head icon on the map.
Definition: GlobalNPC.cs:360
static void ModifyHitPlayer(NPC npc, Player target, ref int damage, ref bool crit)
Definition: NPCLoader.cs:454
virtual void OnGoToStatue(NPC npc, bool toKingStatue)
Allows you to make things happen when this NPC teleports to a King or Queen statue. This method is only called server side.
Definition: GlobalNPC.cs:518
static void ResetEffects(NPC npc)
Definition: NPCLoader.cs:231
static void TownNPCAttackStrength(NPC npc, ref int damage, ref float knockback)
Definition: NPCLoader.cs:1004
static void EditSpawnRange(Player player, ref int spawnRangeX, ref int spawnRangeY, ref int safeRangeX, ref int safeRangeY)
Definition: NPCLoader.cs:755
virtual void TownNPCAttackCooldown(NPC npc, ref int cooldown, ref int randExtraCooldown)
Allows you to determine the cooldown between each of a town NPC&#39;s attack. The cooldown will be a numb...
Definition: GlobalNPC.cs:544
static void DrawHealthBar(NPC npc, Vector2 position, float scale)
Definition: NPCLoader.cs:737
virtual void BuffTownNPC(ref float damageMult, ref int defense)
Allows you to modify the stats of town NPCs. Useful for buffing town NPCs when certain bosses are def...
Definition: GlobalNPC.cs:526
static bool CanHitPlayer(NPC npc, Player target, ref int cooldownSlot)
Definition: NPCLoader.cs:439
static void OnHitPlayer(NPC npc, Player target, int damage, bool crit)
Definition: NPCLoader.cs:464