ItemEntryStack.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * This file is licensed under the MIT License, part of Roughly Enough Items.
  3. * Copyright (c) 2018, 2019, 2020 shedaniel
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. package me.shedaniel.rei.impl;
  24. import com.google.common.collect.Lists;
  25. import com.mojang.blaze3d.platform.GlStateManager;
  26. import me.shedaniel.math.Point;
  27. import me.shedaniel.math.Rectangle;
  28. import me.shedaniel.rei.api.*;
  29. import me.shedaniel.rei.api.widgets.Tooltip;
  30. import net.minecraft.client.MinecraftClient;
  31. import net.minecraft.client.render.OverlayTexture;
  32. import net.minecraft.client.render.VertexConsumerProvider;
  33. import net.minecraft.client.render.model.BakedModel;
  34. import net.minecraft.client.render.model.json.ModelTransformation;
  35. import net.minecraft.client.texture.SpriteAtlasTexture;
  36. import net.minecraft.client.util.math.MatrixStack;
  37. import net.minecraft.item.ItemStack;
  38. import net.minecraft.nbt.CompoundTag;
  39. import net.minecraft.nbt.Tag;
  40. import net.minecraft.text.Text;
  41. import net.minecraft.util.Identifier;
  42. import net.minecraft.util.math.MathHelper;
  43. import net.minecraft.util.registry.Registry;
  44. import org.jetbrains.annotations.ApiStatus;
  45. import org.jetbrains.annotations.Nullable;
  46. import java.util.List;
  47. import java.util.Map;
  48. import java.util.Optional;
  49. import java.util.function.Predicate;
  50. @ApiStatus.Internal
  51. public class ItemEntryStack extends AbstractEntryStack implements OptimalEntryStack {
  52. private static final Predicate<BakedModel> IS_SIDE_LIT = BakedModel::isSideLit;
  53. private ItemStack itemStack;
  54. public ItemEntryStack(ItemStack itemStack) {
  55. this.itemStack = itemStack;
  56. }
  57. @Override
  58. public Optional<Identifier> getIdentifier() {
  59. return Optional.ofNullable(Registry.ITEM.getId(getItem()));
  60. }
  61. @Override
  62. public Type getType() {
  63. return Type.ITEM;
  64. }
  65. @Override
  66. public int getAmount() {
  67. return itemStack.getCount();
  68. }
  69. @Override
  70. public double getFloatingAmount() {
  71. return itemStack.getCount();
  72. }
  73. @Override
  74. public void setAmount(int amount) {
  75. itemStack.setCount(amount);
  76. }
  77. @Override
  78. public void setFloatingAmount(double amount) {
  79. itemStack.setCount(MathHelper.floor(amount));
  80. }
  81. @Override
  82. public boolean isEmpty() {
  83. return itemStack.isEmpty();
  84. }
  85. @Override
  86. public EntryStack copy() {
  87. EntryStack stack = EntryStack.create(getItemStack().copy());
  88. for (Map.Entry<Settings<?>, Object> entry : getSettings().entrySet()) {
  89. stack.setting((Settings<? super Object>) entry.getKey(), entry.getValue());
  90. }
  91. return stack;
  92. }
  93. @Override
  94. public Object getObject() {
  95. return itemStack;
  96. }
  97. /**
  98. * type:
  99. * 0: ignore tags and amount
  100. * 1: ignore tags
  101. * 2: ignore amount
  102. * 3: all
  103. */
  104. private Boolean compareIfFluid(EntryStack stack, int type) {
  105. EntryStack fluid = EntryStack.copyItemToFluid(this);
  106. if (fluid.isEmpty()) return null;
  107. if (stack.getType() == Type.ITEM)
  108. stack = EntryStack.copyItemToFluid(stack);
  109. if (stack.isEmpty()) return null;
  110. switch (type) {
  111. case 0:
  112. return fluid.equalsIgnoreTagsAndAmount(stack);
  113. case 1:
  114. return fluid.equalsIgnoreTags(stack);
  115. case 2:
  116. return fluid.equalsIgnoreAmount(stack);
  117. case 3:
  118. return fluid.equalsAll(stack);
  119. }
  120. return null;
  121. }
  122. @Override
  123. public boolean equalsIgnoreTagsAndAmount(EntryStack stack) {
  124. Boolean ifFluid = compareIfFluid(stack, 0);
  125. if (ifFluid != null) return ifFluid;
  126. if (stack.getType() != Type.ITEM)
  127. return false;
  128. return itemStack.getItem() == stack.getItem();
  129. }
  130. @Override
  131. public boolean equalsAll(EntryStack stack) {
  132. Boolean ifFluid = compareIfFluid(stack, 3);
  133. if (ifFluid != null) return ifFluid;
  134. if (stack.getType() != Type.ITEM)
  135. return false;
  136. return itemStack.getItem() == stack.getItem() && getAmount() != stack.getAmount() && ItemStack.areTagsEqual(itemStack, stack.getItemStack());
  137. }
  138. @Override
  139. public boolean equalsIgnoreAmount(EntryStack stack) {
  140. Boolean ifFluid = compareIfFluid(stack, 2);
  141. if (ifFluid != null) return ifFluid;
  142. if (stack.getType() != Type.ITEM)
  143. return false;
  144. if (itemStack.getItem() != stack.getItem())
  145. return false;
  146. ItemStack otherStack = stack.getItemStack();
  147. CompoundTag o1 = itemStack.getTag();
  148. CompoundTag o2 = otherStack.getTag();
  149. return o1 == o2 || ((o1 != null && o2 != null) && equals(o1, o2));
  150. }
  151. public boolean equals(CompoundTag o1, CompoundTag o2) {
  152. int o1Size = 0;
  153. int o2Size = 0;
  154. for (String key : o1.getKeys()) {
  155. if (key.equals("Count"))
  156. continue;
  157. o1Size++;
  158. }
  159. for (String key : o2.getKeys()) {
  160. if (key.equals("Count"))
  161. continue;
  162. o2Size++;
  163. if (o2Size > o1Size)
  164. return false;
  165. }
  166. if (o1Size != o2Size)
  167. return false;
  168. try {
  169. for (String key : o1.getKeys()) {
  170. if (key.equals("Count"))
  171. continue;
  172. Tag value = o1.get(key);
  173. Tag otherValue = o2.get(key);
  174. if (value == null) {
  175. if (!(otherValue == null && o2.contains(key)))
  176. return false;
  177. } else if (value instanceof CompoundTag && otherValue instanceof CompoundTag) {
  178. if (!(value == otherValue || (value != null && otherValue != null) && equals((CompoundTag) value, (CompoundTag) otherValue)))
  179. return false;
  180. } else {
  181. if (!value.asString().equals(otherValue.asString()))
  182. return false;
  183. }
  184. }
  185. } catch (ClassCastException | NullPointerException unused) {
  186. return false;
  187. }
  188. return true;
  189. }
  190. @Override
  191. public boolean equalsIgnoreTags(EntryStack stack) {
  192. Boolean ifFluid = compareIfFluid(stack, 1);
  193. if (ifFluid != null) return ifFluid;
  194. if (stack.getType() != Type.ITEM)
  195. return false;
  196. if (itemStack.getItem() != stack.getItem())
  197. return false;
  198. return getAmount() == stack.getAmount();
  199. }
  200. @Override
  201. public int hashOfAll() {
  202. int result = hashIgnoreAmount();
  203. result = 31 * result + itemStack.getCount();
  204. return result;
  205. }
  206. @Override
  207. public int hashIgnoreTags() {
  208. int result = hashIgnoreAmountAndTags();
  209. result = 31 * result + itemStack.getCount();
  210. return result;
  211. }
  212. @Override
  213. public int hashIgnoreAmount() {
  214. int result = 1;
  215. result = 31 * result + getType().hashCode();
  216. result = 31 * result + itemStack.getItem().hashCode();
  217. if (itemStack.hasTag()) {
  218. result = 31 * result + itemStack.getTag().asString().hashCode();
  219. } else {
  220. result = 31 * result;
  221. }
  222. return result;
  223. }
  224. @Override
  225. public int hashIgnoreAmountAndTags() {
  226. int result = 1;
  227. result = 31 * result + getType().hashCode();
  228. result = 31 * result + itemStack.getItem().hashCode();
  229. return result;
  230. }
  231. @Nullable
  232. @Override
  233. public Tooltip getTooltip(Point point) {
  234. if (isEmpty() || !get(Settings.TOOLTIP_ENABLED).get())
  235. return null;
  236. List<Text> toolTip = Lists.newArrayList(SearchArgument.tryGetItemStackToolTip(getItemStack(), true));
  237. toolTip.addAll(get(Settings.TOOLTIP_APPEND_EXTRA).apply(this));
  238. if (get(Settings.TOOLTIP_APPEND_MOD).get() && ConfigObject.getInstance().shouldAppendModNames()) {
  239. final Text modString = ClientHelper.getInstance().getFormattedModFromItem(getItem());
  240. final String modId = ClientHelper.getInstance().getModFromItem(getItem());
  241. boolean alreadyHasMod = false;
  242. for (Text s : toolTip)
  243. if (s.asString().equalsIgnoreCase(modId)) {
  244. alreadyHasMod = true;
  245. break;
  246. }
  247. if (!alreadyHasMod)
  248. toolTip.add(modString);
  249. }
  250. return Tooltip.create(toolTip);
  251. }
  252. @Override
  253. public void render(MatrixStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
  254. optimisedRenderStart(matrices, delta);
  255. optimisedRenderBase(matrices, bounds, mouseX, mouseY, delta);
  256. optimisedRenderOverlay(matrices, bounds, mouseX, mouseY, delta);
  257. optimisedRenderEnd(matrices, delta);
  258. }
  259. @SuppressWarnings("deprecation")
  260. @Override
  261. public void optimisedRenderStart(MatrixStack matrices, float delta) {
  262. MinecraftClient.getInstance().getTextureManager().bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX);
  263. GlStateManager.enableRescaleNormal();
  264. }
  265. @SuppressWarnings("deprecation")
  266. @Override
  267. public void optimisedRenderEnd(MatrixStack matrices, float delta) {
  268. GlStateManager.disableRescaleNormal();
  269. }
  270. private BakedModel getModelFromStack(ItemStack stack) {
  271. BakedModel model = MinecraftClient.getInstance().getItemRenderer().getModels().getModel(stack);
  272. if (stack.getItem().hasPropertyGetters())
  273. model = model.getItemPropertyOverrides().apply(model, stack, null, null);
  274. if (model != null)
  275. return model;
  276. return MinecraftClient.getInstance().getItemRenderer().getModels().getModelManager().getMissingModel();
  277. }
  278. @Override
  279. public void optimisedRenderBase(MatrixStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
  280. if (!isEmpty() && get(Settings.RENDER).get()) {
  281. ItemStack stack = getItemStack();
  282. ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(get(Settings.Item.RENDER_ENCHANTMENT_GLINT).get());
  283. matrices.push();
  284. matrices.translate(bounds.getCenterX(), bounds.getCenterY(), 100.0F + getZ());
  285. matrices.scale(bounds.getWidth(), (bounds.getWidth() + bounds.getHeight()) / -2f, bounds.getHeight());
  286. VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
  287. BakedModel model = getModelFromStack(stack);
  288. boolean bl = !IS_SIDE_LIT.test(model);
  289. if (bl)
  290. GlStateManager.setupGuiFlatDiffuseLighting();
  291. MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Mode.GUI, false, matrices, immediate, 15728880, OverlayTexture.DEFAULT_UV, model);
  292. immediate.draw();
  293. if (bl)
  294. GlStateManager.setupGui3dDiffuseLighting();
  295. matrices.pop();
  296. ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(false);
  297. }
  298. }
  299. @Override
  300. public void optimisedRenderOverlay(MatrixStack matrices, Rectangle bounds, int mouseX, int mouseY, float delta) {
  301. if (!isEmpty() && get(Settings.RENDER).get()) {
  302. MinecraftClient.getInstance().getItemRenderer().zOffset = getZ();
  303. MinecraftClient.getInstance().getItemRenderer().renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, getItemStack(), bounds.x, bounds.y, get(Settings.RENDER_COUNTS).get() ? get(Settings.COUNTS).apply(this) : "");
  304. MinecraftClient.getInstance().getItemRenderer().zOffset = 0.0F;
  305. }
  306. }
  307. }