Even More Modifiers  1.0.0.0
A mod for rolling various bonus stats on items
CraftingComponentSelector.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Loot.Api.Core;
5 using Loot.Api.Cubes;
6 using Loot.Api.Ext;
7 using Loot.Api.Mechanism;
8 using Loot.Api.Strategy;
9 using Loot.Cubes;
10 using Loot.Essences;
11 using Loot.Ext;
14 using Microsoft.Xna.Framework;
15 using Terraria;
16 using Terraria.GameContent.UI.Elements;
17 using Terraria.ModLoader;
18 using Terraria.UI;
19 
20 namespace Loot.UI.Tabs.CraftingTab
21 {
22  internal class CraftingComponentSelector<T> : GuiPanel where T : ModItem
23  {
24  public virtual string NotFoundString() => "No components found in inventory";
25  public virtual int ComponentsPerPage() => 5;
26 
27  private readonly List<UIElement> _components = new List<UIElement>();
28  private readonly GuiArrowButton _arrowLeft;
29  private readonly GuiArrowButton _arrowRight;
30  private GuiItemButton _lastSelected;
31 
32  internal List<CraftingComponentLink> ComponentLinks = new List<CraftingComponentLink>();
33  internal Func<Item, IEnumerable<CraftingComponentLink>> FindComponents;
34  internal Func<CraftingComponentLink, Item, bool> VerifyComponent;
35  internal Func<CraftingComponentLink, bool> OnComponentClick;
36 
37  private int _maxOffset;
38  private int _currentOffset;
39 
40  public CraftingComponentSelector()
41  {
42  _arrowLeft = new GuiArrowButton(GuiArrowButton.ArrowDirection.LEFT) { HoverText = "Previous" };
43  _arrowRight = new GuiArrowButton(GuiArrowButton.ArrowDirection.RIGHT) { HoverText = "Next" };
44  OnComponentClick += _OnComponentClick;
45  FindComponents += _FindComponents;
46  VerifyComponent += _VerifyComponent;
47  }
48 
49  public override void OnInitialize()
50  {
51  base.OnInitialize();
52  _arrowLeft.Activate();
53  _arrowLeft.Left.Set(-_arrowLeft.Width.Pixels, 0);
54  _arrowLeft.WhenClicked += delegate (UIMouseEvent evt, UIElement element, GuiArrowButton btn)
55  {
56  _currentOffset--;
57  btn.CanBeClicked = _maxOffset > 0 && _currentOffset > 0;
58  _arrowRight.CanBeClicked = _maxOffset > 0 && _currentOffset < _maxOffset;
59  CalculateAvailableComponents();
60  };
61  Append(_arrowLeft);
62 
63  _arrowRight.Activate();
64  _arrowRight.Left.Set(Width.Pixels, 0);
65  _arrowRight.WhenClicked += delegate (UIMouseEvent evt, UIElement element, GuiArrowButton btn)
66  {
67  _currentOffset++;
68  _arrowLeft.CanBeClicked = _maxOffset > 0 && _currentOffset > 0;
69  btn.CanBeClicked = _maxOffset > 0 && _currentOffset < _maxOffset;
70  CalculateAvailableComponents();
71  };
72  Append(_arrowRight);
73 
74  HAlign = 0.5f;
75  VAlign = 1f;
76  }
77 
78  private bool _OnComponentClick(CraftingComponentLink link)
79  {
80  // When a component is clicked, we need to re-verify that the link to it still exists and is still valid
81  CalculateAvailableComponents();
82  return link?.Component.modItem is MagicalCube && link.Component.stack > 0;
83  }
84 
85  private bool _VerifyComponent(CraftingComponentLink link, Item item)
86  {
87  var props = new RollingStrategyProperties();
88  if (!(link.Component.modItem is T))
89  {
90  return false;
91  }
92 
93  var component = link.Component.modItem;
94  RollingStrategy strategy = null;
95 
96  if (component is CubeOfSealing)
97  {
98  return true;
99  }
100  if (component is RerollingCube cube)
101  {
102  strategy = cube.GetRollingStrategy(item, props);
103  }
104  else if (component is EssenceItem essence)
105  {
106  strategy = essence.GetRollingStrategy(item, props);
107  }
108 
109  var ctx = GetContext(item);
110  ctx.Strategy = strategy;
111  var preRolledLines = strategy?.PreRoll(ModifierPoolMechanism.GetPool(ctx), ctx, props);
112  return preRolledLines?.All(x => x.CanRoll(ctx)) ?? false;
113  }
114 
115  private IEnumerable<CraftingComponentLink> _FindComponents(Item item)
116  {
117  return Main.LocalPlayer.inventory.GetDistinctModItems<T>()
118  .Select(x =>
119  {
120  var component = x as ModItem;
121  component.item.stack = Main.LocalPlayer.inventory.CountItemStack(component.item.type, true);
122  return new CraftingComponentLink(component.item, CraftingComponentLink.ComponentSource.Inventory);
123  });
124  }
125 
126  public bool IsAvailable(int type)
127  {
128  return ComponentLinks.Any(x => x.Component.type == type);
129  }
130 
131  public IEnumerable<CraftingComponentLink> GetComponentLinks(int type)
132  {
133  return ComponentLinks.Where(x => x.Component.type == type);
134  }
135 
136  // TODO select/deselect not working? (scale/color??)
137  public void DeselectPreviousComponent()
138  {
139  if (_lastSelected != null)
140  {
141  _lastSelected.DrawScale = 0.75f;
142  _lastSelected.DynamicScaling = true;
143  _lastSelected.DrawColor = null;
144  _lastSelected = null;
145  }
146  }
147 
148  private void SetSelectedComponent(UIElement element)
149  {
150  if (element != null && element is GuiItemButton itemButton)
151  {
152  DeselectPreviousComponent();
153  itemButton.DrawScale = 0.88f;
154  itemButton.DynamicScaling = false;
155  itemButton.DrawColor = Color.White;
156  _lastSelected = itemButton;
157  }
158  }
159 
160  private ModifierContext GetContext(Item item)
161  {
162  return new ModifierContext
163  {
164  Item = item,
165  Player = Main.LocalPlayer
166  };
167  }
168 
169  public void CalculateAvailableComponents(Item item = null)
170  {
171  _components.Clear();
172  ComponentLinks.Clear();
173 
174  ComponentLinks = FindComponents?.GetInvocationList().SelectMany(x => (IEnumerable<CraftingComponentLink>)x.DynamicInvoke(item)).ToList() ?? new List<CraftingComponentLink>();
175 
176  if (item != null && !item.IsAir)
177  ComponentLinks = ComponentLinks.Where(x => VerifyComponent?.GetInvocationList().Select(y => (bool)y.DynamicInvoke(x, item)).All(z => z) ?? false)
178  .ToList();
179 
180  var selection = ComponentLinks.AsEnumerable();
181 
182  var foundCount = selection.Count();
183  _maxOffset = (int)Math.Floor(foundCount / (ComponentsPerPage() + 1f));
184  if (_maxOffset < _currentOffset) _currentOffset = _maxOffset;
185 
186  if (_currentOffset > 0)
187  {
188  selection = selection.Skip(_currentOffset * ComponentsPerPage());
189  }
190 
191  var items = selection.Take(ComponentsPerPage()).ToList();
192 
193  _arrowLeft.CanBeClicked = _maxOffset > 0 && _currentOffset > 0;
194  _arrowRight.CanBeClicked = _maxOffset > 0 && _currentOffset < _maxOffset;
195 
196  foreach (var link in items)
197  {
198  var name = link.Component.Name;
199  var type = link.Component.type;
200  var stack = link.Component.stack;
201 
202  var button = new GuiItemButton(GuiButton.ButtonType.StoneOuterBevel, type, stack, hintOnHover: $"Click to use {name}")
203  {
204  Left = new StyleDimension(15f, 0f),
205  DrawBackground = false,
206  DrawScale = 0.75f,
207  DrawStack = true,
208  ShowOnlyHintOnHover = true
209  };
210  button.OnMouseOver += (evt, element) => { Main.PlaySound(12); };
211  button.OnClick += (evt, element) =>
212  {
213  if (OnComponentClick?.GetInvocationList().All(x => (bool)x.DynamicInvoke(link)) ?? false)
214  {
215  SetSelectedComponent(element);
216  }
217 
218  };
219  _components.Add(button);
220  }
221 
222  UpdateComponentFrame();
223  }
224 
225  private void UpdateComponentFrame()
226  {
227  Frame.RemoveAllChildren();
228  if (!_components.Any())
229  {
230  Frame.Append(new UIText(NotFoundString()));
231  }
232  else
233  {
234  // TODO
235  //var rememberedSelection = _lastSelected;
236  //DeselectPreviousCube();
237  //SetSelectedCube(rememberedSelection);
238 
239  int i = 0;
240  var elementSet = _components.ToList();
241  foreach (var element in elementSet)
242  {
243  if (i > 0)
244  {
245  element.RightOf(elementSet[i - 1]);
246  }
247  Frame.Append(element);
248  i++;
249  }
250  }
251  }
252  }
253 }
Defines a context in which a Modifier might be rolled Which fields are available (not null) depends o...
Defines properties that will be used when an item is being rolled in a IRollingStrategy<T> These can ...
A cube of sealing is used to lock modifiers in place on an item
Defines a magical cube A magical cube is used to change modifiers on an item
Definition: MagicalCube.cs:12
static ModifierPool GetPool(ModifierContext context)
virtual List< Modifier > PreRoll(ModifierPool drawPool, ModifierContext modifierContext, RollingStrategyProperties properties)
Defines a rerolling cube that opens the rerolling UI on right click The method M:GetRollingStrategy c...