3737@ Mixin (ItemStack .class )
3838public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
3939
40- @ Unique
41- private boolean gtceu$isUpdating = false ;
42-
43- @ Unique
44- private @ Nullable SpoilContext gtceu$lastSpoilContext = new SpoilContext ();
45-
46- @ Unique
47- private boolean gtceu$fakeTooltip ;
48-
49- @ Inject (at = @ At ("RETURN" ),
50- method = "<init>(Lnet/minecraft/world/level/ItemLike;ILnet/minecraft/nbt/CompoundTag;)V" )
51- private void gtceu$injectFakeTooltipInit (ItemLike item , int count , CompoundTag tag , CallbackInfo ci ) {
52- gtceu$fakeTooltip = GTValues .FOOLS .getAsBoolean () && GTValues .RNG .nextFloat () < .1f ;
53- }
40+ // ***************************//
41+ // Shadow fields and methods //
42+ // ***************************//
5443
5544 @ Shadow
5645 public abstract CompoundTag getOrCreateTagElement (String key );
@@ -91,17 +80,77 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
9180 @ Shadow (remap = false )
9281 protected abstract void forgeInit ();
9382
83+ // ***************************//
84+ // Unique fields //
85+ // ***************************//
86+
87+ /**
88+ * Whether {@link ItemStackMixin#gtceu$updateFreshness(SpoilContext, boolean)}
89+ * was called and did not return yet.
90+ * <br>
91+ * Used to prevent stack overflows.
92+ */
93+ @ Unique
94+ private boolean gtceu$isUpdating = false ;
95+
96+ /**
97+ * The value of the last non-empty (determined by {@link SpoilContext#isEmpty()})
98+ * context passed into {@link ItemStackMixin#gtceu$updateFreshness(SpoilContext, boolean)}.
99+ * <br>
100+ * Used to as an argument for {@link ISpoilableItem#spoilResult(SpoilContext, boolean)}
101+ * when this stack spoils.
102+ */
103+ @ Unique
104+ private @ Nullable SpoilContext gtceu$lastSpoilContext = new SpoilContext ();
105+
106+ /**
107+ * Whether to display a fake "spoils into" tooltip for an item if it's not spoilable.
108+ * <br>
109+ * Has a 10% chance of being {@code true} for any stack on april fools.
110+ * The chance is rolled once in the constructor (injected by {@link ItemStackMixin#gtceu$injectFakeTooltipInit}).
111+ */
112+ @ Unique
113+ private boolean gtceu$fakeTooltip ;
114+
94115 @ Unique
95116 private ItemStack gtceu$self () {
96117 return (ItemStack ) (Object ) this ;
97118 }
98119
120+ // ***************************//
121+ // Interface implementations //
122+ // ***************************//
123+
124+ /**
125+ * Primarily used as debug info when advanced tooltips are turned on.
126+ *
127+ * @return the last known context of this stack
128+ */
99129 @ Unique
100130 @ Override
101131 public SpoilContext gtceu$getSpoilContext () {
102132 return gtceu$lastSpoilContext ;
103133 }
104134
135+ /**
136+ * Checks if this item should've already spoiled, and calls
137+ * {@link ISpoilableItem#spoilResult(SpoilContext, boolean)}
138+ * with {@link ItemStackMixin#gtceu$lastSpoilContext}
139+ * and replaces this item with its return value if so.<br>
140+ * Also sets {@link ItemStackMixin#gtceu$lastSpoilContext} to the provided
141+ * context if it is non-empty (determined by {@link SpoilContext#isEmpty()}).<br>
142+ * <br>
143+ * If {@code createTag = true} and the spoilage tag did not exist, creates
144+ * the tag and sets the creation tick to this tick.<br>
145+ * If {@code createTag = false} and the spoilage tag isn't present, does nothing.
146+ *
147+ * @param createTag Whether to create a spoilage tag if it wasn't present.
148+ * Usually {@code true} for stacks that are present in-world,
149+ * and {@code false} for stacks in XEI, icons, quests, etc.
150+ *
151+ * @implNote This method is injected into all of {@link ItemStack}'s getters to
152+ * be called with an empty {@link SpoilContext} and {@code createTag = false}.
153+ */
105154 @ Unique
106155 @ Override
107156 public void gtceu$updateFreshness (@ NotNull SpoilContext spoilContext , boolean createTag ) {
@@ -143,14 +192,19 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
143192 try {
144193 gtceu$isUpdating = false ;
145194 gtceu$updateFreshness (spoilContext , false );
146- } catch (StackOverflowError ignored ) {} // if some crazy pack dev makes an item spoil into a
147- // spoilable that spoils into a spoilable 1000 times
195+ } catch (StackOverflowError ignored ) {
196+ // if items spoil in a giant chain or a loop
197+ }
148198 }
149199 }
150200 }
151201 gtceu$isUpdating = false ;
152202 }
153203
204+ // ***************************//
205+ // Injectors //
206+ // ***************************//
207+
154208 @ Inject (at = @ At ("HEAD" ),
155209 method = { "getItem" , "getCount" , "getTag" , "getOrCreateTag" , "getTagElement" , "getOrCreateTagElement" ,
156210 "getItemHolder" })
@@ -172,6 +226,18 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
172226 gtceu$updateFreshness (new SpoilContext (player , -1 ), true );
173227 }
174228
229+ /**
230+ * Since {@link ItemStack#isSameItemSameTags(ItemStack, ItemStack)} is commonly called
231+ * right before merging two stacks, this method averages their spoil progress (or, more
232+ * accurately, their {@link ISpoilableItem#getCreationTick()}). If {@link SpoilUtils#FROZEN_EQUALITY}
233+ * is {@code true}, this method will ignore the frozen/not frozen status of stacks when determining its
234+ * return value. Other than that, the return value is equal to the normal {@link ItemStack#isSameItemSameTags}.
235+ * <br>
236+ * This method replaces {@link ItemStack#isSameItemSameTags(ItemStack, ItemStack)}.
237+ *
238+ * @implNote This implementation may lead to spoil progress averaging in situations other
239+ * than stack merging, though I don't think this will lead to any big user-facing bugs.
240+ */
175241 @ Inject (at = @ At ("HEAD" ), method = "isSameItemSameTags" , cancellable = true )
176242 private static void gtceu$mergeSpoilables (ItemStack stack , ItemStack other , CallbackInfoReturnable <Boolean > cir ) {
177243 ISpoilableItem spoilable1 = GTCapabilityHelper .getSpoilable (stack );
@@ -208,13 +274,23 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
208274 cir .setReturnValue (isSameItem && Objects .equals (stack .getTag (), other .getTag ()));
209275 }
210276
277+ @ Inject (at = @ At ("RETURN" ),
278+ method = "<init>(Lnet/minecraft/world/level/ItemLike;ILnet/minecraft/nbt/CompoundTag;)V" )
279+ private void gtceu$injectFakeTooltipInit (ItemLike item , int count , CompoundTag tag , CallbackInfo ci ) {
280+ gtceu$fakeTooltip = GTValues .FOOLS .getAsBoolean () && GTValues .RNG .nextFloat () < .1f ;
281+ }
282+
283+ /**
284+ * Allows {@link ISpoilableItem} subclasses that implement {@link IAddInformation} to
285+ * actually display the added tooltip.
286+ */
211287 @ Inject (at = @ At (value = "INVOKE" ,
212288 target = "Lnet/minecraft/world/item/Item;appendHoverText(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/Level;Ljava/util/List;Lnet/minecraft/world/item/TooltipFlag;)V" ),
213289 method = "getTooltipLines" ,
214290 locals = LocalCapture .CAPTURE_FAILSOFT )
215- private void gtceu$aprilFoolsTooltip (Player player , TooltipFlag isAdvanced ,
216- CallbackInfoReturnable <List <Component >> cir ,
217- List <Component > list ) {
291+ private void gtceu$spoilageTooltip (Player player , TooltipFlag isAdvanced ,
292+ CallbackInfoReturnable <List <Component >> cir ,
293+ List <Component > list ) {
218294 ISpoilableItem spoilable = GTCapabilityHelper .getSpoilable (gtceu$self ());
219295 if (!(getItem () instanceof ISpoilableItem ) && spoilable instanceof IAddInformation addInformation ) {
220296 addInformation .appendHoverText (gtceu$self (), player == null ? null : player .level (), list ,
@@ -229,8 +305,12 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
229305 }
230306 }
231307
308+ /**
309+ * Allows {@link ISpoilableItem} subclasses that implement {@link IDurabilityBar} to
310+ * actually display the bar.
311+ */
232312 @ Inject (at = @ At ("HEAD" ), method = "isBarVisible" , cancellable = true )
233- private void gtceu$aprilFoolsBarVisible (CallbackInfoReturnable <Boolean > cir ) {
313+ private void gtceu$spoilageBarVisible (CallbackInfoReturnable <Boolean > cir ) {
234314 ISpoilableItem spoilable = GTCapabilityHelper .getSpoilable (gtceu$self ());
235315 if (!(getItem () instanceof ISpoilableItem ) && spoilable instanceof IDurabilityBar durabilityBar ) {
236316 cir .setReturnValue (durabilityBar .isBarVisible (gtceu$self ()));
@@ -239,8 +319,12 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
239319 }
240320 }
241321
322+ /**
323+ * Allows {@link ISpoilableItem} subclasses that implement {@link IDurabilityBar} to
324+ * actually display the bar.
325+ */
242326 @ Inject (at = @ At ("HEAD" ), method = "getBarColor" , cancellable = true )
243- private void gtceu$aprilFoolsBarColor (CallbackInfoReturnable <Integer > cir ) {
327+ private void gtceu$spoilageBarColor (CallbackInfoReturnable <Integer > cir ) {
244328 ISpoilableItem spoilable = GTCapabilityHelper .getSpoilable (gtceu$self ());
245329 if (!(getItem () instanceof ISpoilableItem ) && spoilable instanceof IDurabilityBar durabilityBar ) {
246330 cir .setReturnValue (durabilityBar .getBarColor (gtceu$self ()));
@@ -249,8 +333,12 @@ public abstract class ItemStackMixin implements ISpoilableItemStackExtension {
249333 }
250334 }
251335
336+ /**
337+ * Allows {@link ISpoilableItem} subclasses that implement {@link IDurabilityBar} to
338+ * actually display the bar.
339+ */
252340 @ Inject (at = @ At ("HEAD" ), method = "getBarWidth" , cancellable = true )
253- private void gtceu$aprilFoolsBarWidth (CallbackInfoReturnable <Integer > cir ) {
341+ private void gtceu$spoilageBarWidth (CallbackInfoReturnable <Integer > cir ) {
254342 ISpoilableItem spoilable = GTCapabilityHelper .getSpoilable (gtceu$self ());
255343 if (!(getItem () instanceof ISpoilableItem ) && spoilable instanceof IDurabilityBar durabilityBar ) {
256344 cir .setReturnValue (durabilityBar .getBarWidth (gtceu$self ()));
0 commit comments