Even More Modifiers  1.0.0.0
A mod for rolling various bonus stats on items
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Pages
ModifierCachePlayer.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using Loot.Api.Attributes;
6 using Loot.Api.Core;
7 using Loot.Api.Delegators;
8 using Loot.Api.Ext;
9 using Loot.Hacks;
10 using Loot.ModSupport;
11 using Terraria;
12 using Terraria.ModLoader;
13 
14 namespace Loot.Caching
15 {
16  /*
17  * Do note that this class is very important
18  * And without this class, any of the delegations will not work at all
19  * This class automatically delegates only the applicable modifiers and their effects
20  * \\WHEN NEEDED\\. This means only 'active' items are being updated, that means any held item
21  * and items that are equipped (armor/accessory)
22  */
23 
28  public sealed partial class ModifierCachePlayer : ModPlayer
29  {
30  private static bool IsMouseUsable(Item item) => item.damage > 0;
31 
32  internal bool Ready;
33 
34  private int _oldSelectedItem;
35  private Item _oldMouseItem;
36  private Item[] _oldEquips;
37  private Item[] _oldVanityEquips;
38  private bool _forceEquipUpdate;
39  private Item[] _oldCheatSheetEquips;
40  private List<Type> _modifierEffects;
41  private List<(Item, Modifier)> _detachList;
42  private List<(Item, Modifier)> _attachList;
43 
44  public override void Initialize()
45  {
46  _oldSelectedItem = player.selectedItem;
47  _oldMouseItem = null;
48  _oldEquips = new Item[8];
49  _oldVanityEquips = new Item[8];
50  _forceEquipUpdate = false;
51  _oldCheatSheetEquips = new Item[6]; // MaxExtraAccessories = 6
52  _modifierEffects = new List<Type>();
53  _detachList = new List<(Item, Modifier)>();
54  _attachList = new List<(Item, Modifier)>();
55  Ready = false;
56  }
57 
61  private IEnumerable<ModifierEffect> GetModifierEffectsForDelegations(IEnumerable<(Item item, Modifier modifier)> list, ModifierDelegatorPlayer modDelegatorPlayer, Func<ModifierEffect, bool> conditionFunc)
62  {
63  var tempList = new List<ModifierEffect>();
64  foreach (var delegationTuple in list)
65  {
66  var effectsAttribute =
67  delegationTuple.modifier
68  .GetType()
69  .GetCustomAttribute<UsesEffectAttribute>();
70 
71  if (effectsAttribute == null) continue;
72  tempList.AddRange(effectsAttribute.Effects
73  .Select(modDelegatorPlayer.GetEffect)
74  .Where(modEffect => modEffect != null && conditionFunc.Invoke(modEffect)));
75  }
76 
77  return tempList;
78  }
79 
90  private void UpdateAttachments()
91  {
92  ModifierDelegatorPlayer modDelegatorPlayer = ModifierDelegatorPlayer.GetPlayer(player);
93 
94  // Manual detach
95  var detachEffects = GetModifierEffectsForDelegations(_detachList, modDelegatorPlayer, (e) => e.IsBeingDelegated && !_modifierEffects.Contains(e.GetType()));
96  var attachEffects = GetModifierEffectsForDelegations(_attachList, modDelegatorPlayer, (e) => !e.IsBeingDelegated);
97  // Automatic delegation lists
98  var orderedDetachList = OrderDelegationList(_detachList, modDelegatorPlayer)
99  .Where(x => x.effect.IsBeingDelegated && !_modifierEffects.Contains(x.effect.GetType()))
100  .GroupBy(x => x.methodInfo)
101  .Select(x => x.First())
102  .ToList();
103 
104  var orderedAttachList = OrderDelegationList(_attachList, modDelegatorPlayer)
105  .Where(x => !x.effect.IsBeingDelegated)
106  .GroupBy(x => x.methodInfo)
107  .Select(x => x.First())
108  .ToList();
109 
110  // Manual detach
111  foreach (var effect in detachEffects.Distinct())
112  {
113  modDelegatorPlayer.ResetEffectsEvent -= effect.ResetEffects;
114  effect._DetachDelegations(modDelegatorPlayer);
115  effect.IsBeingDelegated = false;
116  }
117 
118  // Manual attach
119  foreach (var effect in attachEffects.Distinct())
120  {
121  modDelegatorPlayer.ResetEffectsEvent += effect.ResetEffects;
122  effect.AttachDelegations(modDelegatorPlayer);
123  effect.IsBeingDelegated = true;
124  }
125 
126  // Auto delegation detach
127  foreach (var (methodInfo, effect) in orderedDetachList)
128  {
129  var attr = methodInfo.GetCustomAttribute<AutoDelegation>();
130  attr.Detach(modDelegatorPlayer, methodInfo, effect);
131  }
132 
133  // Auto delegation attach
134  foreach (var (methodInfo, effect) in orderedAttachList)
135  {
136  var attr = methodInfo.GetCustomAttribute<AutoDelegation>();
137  attr.Attach(modDelegatorPlayer, methodInfo, effect);
138  }
139  }
140 
146  private IEnumerable<(MethodInfo methodInfo, ModifierEffect effect)> OrderDelegationList(IEnumerable<(Item item, Modifier modifier)> list, ModifierDelegatorPlayer modDelegatorPlayer)
147  {
148  var delegationEntries = new List<(MethodInfo, ModifierEffect)>();
149  var effects = GetModifierEffectsForDelegations(list, modDelegatorPlayer, e => true);
150 
151  foreach (var modEffect in effects)
152  {
153  (MethodInfo, ModifierEffect) MakeEntry((MethodInfo, DelegationPrioritizationAttribute) tuple)
154  => (tuple.Item1, modEffect);
155 
156  var delegatedMethods = modEffect
157  .GetType()
158  .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
159  .Where(x => x.GetCustomAttributes(typeof(AutoDelegation), false).Length > 0)
160  .Select(x => (methodInfo: x, attribute: (DelegationPrioritizationAttribute) x.GetCustomAttribute(typeof(DelegationPrioritizationAttribute))))
161  .ToList();
162 
163  delegationEntries.AddRange(
164  delegatedMethods
165  .Where(x => x.attribute?.DelegationPrioritization == DelegationPrioritization.Early)
166  .OrderByDescending(x => x.attribute.DelegationLevel)
167  .Select(MakeEntry));
168 
169  delegationEntries.AddRange(
170  delegatedMethods
171  .Where(x => x.attribute == null)
172  .Select(MakeEntry));
173 
174  delegationEntries.AddRange(
175  delegatedMethods
176  .Where(x => x.attribute?.DelegationPrioritization == DelegationPrioritization.Late)
177  .OrderByDescending(x => x.attribute.DelegationLevel)
178  .Select(MakeEntry));
179  }
180 
181  return delegationEntries;
182  }
183 
184  public override void PostUpdate()
185  {
186  // If our equips cache is not the right size we resize it and force an update
187  if (_oldEquips.Length != 8 + player.extraAccessorySlots)
188  {
189  Ready = false;
190  _forceEquipUpdate = true;
191  Array.Resize(ref _oldEquips, 8 + player.extraAccessorySlots);
192  }
193 
194  if (_oldVanityEquips.Length != 8 + player.extraAccessorySlots)
195  {
196  Ready = false;
197  _forceEquipUpdate = true;
198  Array.Resize(ref _oldVanityEquips, 8 + player.extraAccessorySlots);
199  }
200 
201  _detachList.Clear();
202  _attachList.Clear();
203 
204  UpdateEquipsCache();
205  if (ModSupportTunneler.GetModSupport<CheatSheetSupport>().ModIsLoaded)
206  {
207  UpdateCheatSheetCache();
208  }
209 
210  UpdateVanityCache();
211 
212  if (!UpdateMouseItemCache())
213  {
214  UpdateHeldItemCache();
215  }
216 
217  CacheModifierEffects();
218  UpdateAttachments();
219 
220  _forceEquipUpdate = false;
221  Ready = true;
222  }
223 
224  private void AddDetachItem(Item item, Modifier modifier)
225  {
226  LootModItem.GetInfo(item).IsActivated = false;
227  _detachList.Add((item, modifier));
228  }
229 
230  private void AddAttachItem(Item item, Modifier modifier)
231  {
232  LootModItem.GetInfo(item).IsActivated = true;
233  _attachList.Add((item, modifier));
234  }
235 
236  private void CacheModifierEffects()
237  {
238  if (!_forceEquipUpdate && Ready)
239  return;
240 
241  _modifierEffects.Clear();
242 
243  for (int i = 0; i < 8 + player.extraAccessorySlots; i++)
244  {
245  var equip = player.armor[i];
246  if (equip != null && !equip.IsAir)
247  {
248  CacheItemModifierEffects(equip);
249  }
250  }
251 
252  // vanity
253  for (int k = 13; k < 18 + player.extraAccessorySlots; k++)
254  {
255  var equip = player.armor[k];
256  if (equip != null
257  && !equip.IsAir
259  {
260  CacheItemModifierEffects(equip);
261  }
262  }
263 
264  if (Main.mouseItem != null && !Main.mouseItem.IsAir && Main.mouseItem.IsWeapon())
265  {
266  CacheItemModifierEffects(Main.mouseItem);
267  }
268  else if (player.HeldItem != null && !player.HeldItem.IsAir && player.HeldItem.IsWeapon())
269  {
270  CacheItemModifierEffects(player.HeldItem);
271  }
272  }
273 
274  private void CacheItemModifierEffects(Item item)
275  {
276  var mods = LootModItem.GetActivePool(item);
277  foreach (var modifier in mods)
278  {
279  var effectsAttribute = modifier
280  .GetType()
281  .GetCustomAttribute<UsesEffectAttribute>();
282 
283  if (effectsAttribute == null)
284  continue;
285 
286  ModifierDelegatorPlayer modDelegatorPlayer = ModifierDelegatorPlayer.GetPlayer(player);
287  foreach (Type effect in effectsAttribute.Effects)
288  {
289  var modEffect = modDelegatorPlayer.GetEffect(effect);
290  if (modEffect != null && !_modifierEffects.Contains(modEffect.GetType()))
291  {
292  _modifierEffects.Add(modEffect.GetType());
293  }
294  }
295  }
296  }
297  }
298 }
Defines a modifier, which is an unloaded GlobalItem Making it a GlobalItem gives easy access to all h...
Definition: Modifier.cs:21
void AddAttachItem(Item item, Modifier modifier)
This attribute may be used to skip usage of AttachDelegations and DetachDelegations Which is a cumber...
List<(Item, Modifier)> _detachList
void UpdateAttachments()
This method handles updating all delegations First, it will perform &#39;manual&#39; detachments calling Modi...
A ModifierEffect signifies the effect of a modifier on a player It should house the implementation...
void AddDetachItem(Item item, Modifier modifier)
void Detach(ModifierDelegatorPlayer delegatorPlayer, MethodInfo method, ModifierEffect effect)
IEnumerable< ModifierEffect > GetModifierEffectsForDelegations(IEnumerable<(Item item, Modifier modifier)> list, ModifierDelegatorPlayer modDelegatorPlayer, Func< ModifierEffect, bool > conditionFunc)
This method will return a list of ModifierEffects based on Modifiers passed to it ...
Can detect if an item was activated via cheats
DelegationPrioritization
Specify when you want your delegation to happen in the chain
void Attach(ModifierDelegatorPlayer delegatorPlayer, MethodInfo method, ModifierEffect effect)
Defines an item that may be modified by modifiers from mods
Definition: LootModItem.cs:21
static ModifierDelegatorPlayer GetPlayer(Player player)
static List< Modifier > GetActivePool(Item item)
static LootModItem GetInfo(Item item)
IEnumerable<(MethodInfo methodInfo, ModifierEffect effect)> OrderDelegationList(IEnumerable<(Item item, Modifier modifier)> list, ModifierDelegatorPlayer modDelegatorPlayer)
This method orders delegation entries following the rules of DelegationPrioritizationAttribute which ...
List<(Item, Modifier)> _attachList
static CheatedItemHackGlobalItem GetInfo(Item item)
bool IsActivated
Keeps track of if the particular item was activated (&#39;delegated&#39;) Specific usecase see CursedEffect a...
Definition: LootModItem.cs:45
This attribute is used to set a custom prioritization for a delegation It allows you to customize at ...
bool IsCheated
Keeps track of if the item was activated (by another mod) In this case activated means giving its reg...
Holds player-entity data and handles it
This attribute is used to attach a certain Modifier to given ModifierEffects (can be attached to more...