Even More Modifiers  1.0.0.0
A mod for rolling various bonus stats on items
CraftingTab.cs
Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Linq;
3 using Loot.Api.Core;
4 using Loot.Api.Cubes;
5 using Loot.Api.Mechanism;
6 using Loot.Api.Strategy;
7 using Loot.Ext;
9 using Loot.Sounds;
12 using Loot.UI.Tabs.Cubing;
13 using Microsoft.Xna.Framework.Graphics;
14 using Terraria;
15 using Terraria.ID;
16 using Terraria.ModLoader;
17 using Terraria.UI;
18 
19 namespace Loot.UI.Tabs.CraftingTab
20 {
21  // Because fuck generics
22  interface ICraftingTab
23  {
24  bool AcceptsItem(Item item);
25  void GiveBackItems();
26  void OverrideSlottedItem(Item newItem);
27  }
28 
33  internal abstract class CraftingTab<T> : GuiTab, ICraftingTab where T : ModItem
34  {
35  public override int GetPageHeight() => 100;
36 
37  internal abstract CraftingComponentButton GetComponentButton();
38  protected abstract ModifierContextMethod CraftMethod { get; }
39 
40  protected virtual Dictionary<string, object> CustomData => new Dictionary<string, object>
41  {
42  {
43  "Source", "Crafting"
44  }
45  };
46 
47  public virtual bool AcceptsItem(Item item)
48  {
49  return ItemButton?.CanTakeItem(item) ?? false;
50  }
51 
52  internal CraftingComponentButton ComponentButton;
53  internal GuiCraftItemButton ItemButton;
55  internal CraftingComponentSelector<T> ComponentSelector;
56 
57  protected GuiImageButton _craftButton;
58  protected GuiTextPanel[] _modifierPanels;
59 
60  public override void OnInitialize()
61  {
62  base.OnInitialize();
63 
64  RollingStrategyProperties = new RollingStrategyProperties();
65 
66  ComponentButton = GetComponentButton();
67  ComponentButton.OnItemChange += item =>
68  {
69  ComponentSelector.CalculateAvailableComponents(ItemButton.Item);
70  if (!ItemButton.Item.IsAir && !IsStrategyAllowed())
71  {
72  ComponentButton.SetLink(null);
73  }
74  };
75  TabFrame.Append(ComponentButton);
76 
77  ItemButton = new GuiCraftItemButton(
78  GuiButton.ButtonType.Parchment,
79  hintTexture: ModContent.GetTexture("Terraria/Item_24"),
80  hintText: "Place an item here"
81  );
82  ItemButton.CanTakeItemAction += IsStrategyAllowed;
83  ItemButton.PreOnClickAction += () =>
84  {
85  if (!ItemButton.Item.IsAir)
86  {
87  LootModItem.GetInfo(ItemButton.Item).SlottedInUI = false;
88  }
89  };
90  ItemButton.OnItemChange += item =>
91  {
92  ComponentSelector.CalculateAvailableComponents(ItemButton.Item);
93  if (!ComponentSelector.IsAvailable(ComponentButton.Item.type))
94  {
95  ComponentButton.SetLink(null);
96  }
97 
98  if (!item.IsAir)
99  {
100  LootModItem.GetInfo(item).SlottedInUI = true;
101  }
102  UpdateModifiersInGui();
103  };
104  TabFrame.Append(ItemButton.Below(ComponentButton));
105 
106  _modifierPanels = new GuiTextPanel[4];
107  for (int i = 0; i < 4; i++)
108  {
109  _modifierPanels[i] = new GuiTextPanel();
110  var panel = _modifierPanels[i].RightOf(ComponentButton);
111  if (i > 0)
112  {
113  panel.Below(_modifierPanels[i - 1]);
114  }
115 
116  TabFrame.Append(panel);
117  }
118 
119  Texture2D craftTexture = ModContent.GetTexture("Terraria/UI/Craft_Toggle_3");
120  _craftButton = new GuiImageButton(GuiButton.ButtonType.StoneOuterBevel, craftTexture);
121  _craftButton.OnClick += (evt, element) =>
122  {
123  ComponentSelector.CalculateAvailableComponents(ItemButton.Item);
124  if (!ComponentSelector.IsAvailable(ComponentButton.Item.type))
125  {
126  ComponentButton.SetLink(null);
127  }
128 
129  HandleCraftButtonClick(evt, element);
130  };
131 
132  TabFrame.Append(_craftButton.Below(ItemButton));
133 
134  ComponentSelector = new CraftingComponentSelector<T>();
135  ComponentSelector.OnComponentClick += link =>
136  {
137  if (link == null) return false;
138  ComponentButton.SetLink(link);
139  return true;
140  };
141  Height.Set(Height.Pixels + ComponentSelector.Height.Pixels, 0);
142  Append(ComponentSelector);
143  }
144 
145  private bool CraftRequirementsMet()
146  {
147  return ComponentButton.Link?.Component.modItem is MagicalCube
148  && ItemButton.CanTakeItem(ItemButton.Item)
149  && ComponentButton.CanTakeItem(ComponentButton.Item)
150  && IsStrategyAllowed();
151  }
152 
153  // TODO make this better
154  private bool IsStrategyAllowed(Item overrideItem = null)
155  {
156  RollingStrategyProperties = new RollingStrategyProperties();
157  var ctx = GetContext(overrideItem ?? ItemButton.Item);
158  var strategy = ctx.Strategy;
159  var preRolledLines = strategy.PreRoll(ModifierPoolMechanism.GetPool(ctx), ctx, RollingStrategyProperties);
160  return preRolledLines.All(x => x.CanRoll(ctx));
161  }
162 
163  public override void OnShow()
164  {
165  ComponentSelector?.CalculateAvailableComponents(ItemButton.Item);
166  UpdateModifiersInGui();
167  }
168 
169  private ModifierContext GetContext(Item item)
170  {
171  return new ModifierContext
172  {
173  Method = CraftMethod,
174  Item = item,
175  Player = Main.LocalPlayer,
176  CustomData = this.CustomData,
177  Strategy = ComponentButton.GetRollingStrategy(ItemButton.Item, RollingStrategyProperties)
178  };
179  }
180 
181  private void HandleCraftButtonClick(UIMouseEvent evt, UIElement listeningElement)
182  {
183  if (!CraftRequirementsMet())
184  {
185  SoundHelper.PlayCustomSound(SoundHelper.SoundType.Decline);
186  return;
187  }
188 
189  RollingStrategyProperties = new RollingStrategyProperties();
190 
191  if (ComponentButton.Item.stack <= 0)
192  {
193  SoundHelper.PlayCustomSound(SoundHelper.SoundType.Decline);
194  ComponentButton.SetLink(null);
195  return;
196  }
197 
198  var item = GetClonedItem(ItemButton.Item);
199  var info = LootModItem.GetInfo(item);
200  var strategy = ComponentButton.GetRollingStrategy(item, RollingStrategyProperties);
201 
202  // Sealed and not allowed to roll
203  if (info.SealedModifiers && !(strategy is SealingRollingStrategy))
204  {
205  SoundHelper.PlayCustomSound(SoundHelper.SoundType.Decline);
206  return;
207  }
208 
209  // Roll new lines
210  var context = GetContext(item);
211  var rolled = strategy._Roll(ModifierPoolMechanism.GetPool(context), context, RollingStrategyProperties);
212  LootModItem.GetInfo(item).Modifiers = NullModifierPool.INSTANCE; // unload previous pool
213  if (rolled.Any())
214  {
215  item.UpdateModifiers(rolled);
216  }
217  strategy.PlaySoundEffect(item);
218  ItemButton.Item = item;
219 
220  ConsumeComponents();
221  UpdateModifiersInGui();
222  }
223 
224  private Item GetClonedItem(Item toClone)
225  {
226  var clone = new Item();
227  clone.netDefaults(toClone.type);
228  // Clone to preserve modded data
229  clone = clone.CloneWithModdedDataFrom(ItemButton.Item);
230  // Restore prefix
231  if (ItemButton.Item.prefix > 0)
232  {
233  clone.Prefix(toClone.prefix);
234  }
235 
236  clone.stack = toClone.stack;
237  return clone;
238  }
239 
240  public void OverrideSlottedItem(Item newItem)
241  {
242  ItemButton.ChangeItem(0, newItem.Clone());
243  LootModItem.GetInfo(ItemButton.Item).SlottedInUI = true;
244  UpdateModifiersInGui();
245  Main.PlaySound(SoundID.Grab);
246  }
247 
248  private void UpdateModifiersInGui()
249  {
250  foreach (var panel in _modifierPanels)
251  {
252  panel.ResetText();
253  }
254 
255  if (ItemButton.Item.IsAir || _modifierPanels == null)
256  {
257  return;
258  }
259 
260  int i = 0;
261 
262  IEnumerable<ModifierTooltipLine[]> GetTooltipLines(Item item)
263  => LootModItem.GetActivePool(item)
264  .Select(x => x.GetTooltip().Build().ToArray())
265  .Take(4)
266  .Where(x => x != null);
267 
268  foreach (var lines in GetTooltipLines(ItemButton.Item))
269  {
270  string line = lines.Aggregate("", (current, tooltipLine) => current + $"{tooltipLine.Text} ");
271  line = line.TrimEnd();
272  var measure = Main.fontMouseText.MeasureString(line);
273  if (measure.X >= _modifierPanels[i].Width.Pixels + SPACING * 4)
274  {
275  _modifierPanels[i].SetHoverText(line);
276  line = Main.fontMouseText.CreateWrappedText(line, _modifierPanels[i].Width.Pixels)
277  .Split('\n')[0] + "...";
278  }
279  else
280  {
281  _modifierPanels[i].SetHoverText(null);
282  }
283 
284  _modifierPanels[i].UpdateText(line);
285  i++;
286  }
287 
288  Recalculate();
289  }
290 
291  private void ConsumeComponents()
292  {
293  ComponentButton.Link.Component.stack--;
294  ComponentButton.Item.stack--;
295  if (ComponentButton.Link.Component.stack <= 0)
296  {
297  ComponentButton.SetLink(null);
298  }
299  ComponentSelector.CalculateAvailableComponents(ItemButton.Item);
300  }
301 
302  public override void GiveBackItems()
303  {
304  if (!ItemButton?.Item?.IsAir ?? false)
305  {
306  // Runs only in SP or client, so this is safe
307  ItemButton.Item.noGrabDelay = 0;
308  Main.LocalPlayer.GetItem(Main.myPlayer, GetClonedItem(ItemButton.Item));
309  ItemButton.Item.TurnToAir();
310  }
311  }
312  }
313 }
Defines a context in which a Modifier might be rolled Which fields are available (not null) depends o...
Defines a "Null" modifier which represents a pool with no modifiers Cannot be rolled normally ...
static NullModifierPool INSTANCE
Defines properties that will be used when an item is being rolled in a IRollingStrategy<T> These can ...
ModifierContextMethod
Defines a method for a context in which a Modifier might be rolled Used in ModifierContext ...
Defines an item that may be modified by modifiers from mods
Definition: LootModItem.cs:21
Defines a magical cube A magical cube is used to change modifiers on an item
Definition: MagicalCube.cs:12
static ModifierPool GetPool(ModifierContext context)
static List< Modifier > GetActivePool(Item item)
static LootModItem GetInfo(Item item)
FiniteModifierPool Modifiers
Definition: LootModItem.cs:32