FluidEntryStack.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 me.shedaniel.math.Point;
  26. import me.shedaniel.math.Rectangle;
  27. import me.shedaniel.rei.api.ClientHelper;
  28. import me.shedaniel.rei.api.ConfigObject;
  29. import me.shedaniel.rei.api.EntryStack;
  30. import me.shedaniel.rei.api.widgets.Tooltip;
  31. import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
  32. import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
  33. import net.minecraft.client.MinecraftClient;
  34. import net.minecraft.client.render.BufferBuilder;
  35. import net.minecraft.client.render.Tessellator;
  36. import net.minecraft.client.render.VertexFormats;
  37. import net.minecraft.client.texture.Sprite;
  38. import net.minecraft.client.texture.SpriteAtlasTexture;
  39. import net.minecraft.fluid.Fluid;
  40. import net.minecraft.fluid.Fluids;
  41. import net.minecraft.util.Identifier;
  42. import net.minecraft.util.Pair;
  43. import net.minecraft.util.math.BlockPos;
  44. import net.minecraft.util.registry.Registry;
  45. import org.jetbrains.annotations.ApiStatus;
  46. import org.jetbrains.annotations.Nullable;
  47. import java.util.*;
  48. @ApiStatus.Internal
  49. public class FluidEntryStack extends AbstractEntryStack {
  50. private static final Map<Fluid, Pair<Sprite, Integer>> FLUID_SPRITE_CACHE = new HashMap<>();
  51. private static final int EMPTY_AMOUNT = -1319182373;
  52. private Fluid fluid;
  53. private int amount;
  54. public FluidEntryStack(Fluid fluid) {
  55. this(fluid, EMPTY_AMOUNT);
  56. }
  57. public FluidEntryStack(Fluid fluid, int amount) {
  58. this.fluid = fluid;
  59. this.amount = amount;
  60. }
  61. protected static Pair<Sprite, Integer> getOrLoadSprite(Fluid fluid) {
  62. Pair<Sprite, Integer> possibleCached = FLUID_SPRITE_CACHE.get(fluid);
  63. if (possibleCached != null)
  64. return possibleCached;
  65. FluidRenderHandler fluidRenderHandler = FluidRenderHandlerRegistry.INSTANCE.get(fluid);
  66. if (fluidRenderHandler == null)
  67. return null;
  68. Sprite[] sprites = fluidRenderHandler.getFluidSprites(MinecraftClient.getInstance().world, MinecraftClient.getInstance().world == null ? null : BlockPos.ORIGIN, fluid.getDefaultState());
  69. int color = -1;
  70. if (MinecraftClient.getInstance().world != null)
  71. color = fluidRenderHandler.getFluidColor(MinecraftClient.getInstance().world, BlockPos.ORIGIN, fluid.getDefaultState());
  72. Pair<Sprite, Integer> pair = new Pair<>(sprites[0], color);
  73. FLUID_SPRITE_CACHE.put(fluid, pair);
  74. return pair;
  75. }
  76. @Override
  77. public Optional<Identifier> getIdentifier() {
  78. return Optional.ofNullable(Registry.FLUID.getId(getFluid()));
  79. }
  80. @Override
  81. public Type getType() {
  82. return Type.FLUID;
  83. }
  84. @Override
  85. public int getAmount() {
  86. return amount;
  87. }
  88. @Override
  89. public void setAmount(int amount) {
  90. this.amount = amount == EMPTY_AMOUNT ? EMPTY_AMOUNT : Math.max(amount, 0);
  91. if (isEmpty()) {
  92. fluid = Fluids.EMPTY;
  93. }
  94. }
  95. @Override
  96. public boolean isEmpty() {
  97. return (amount != EMPTY_AMOUNT && amount <= 0) || fluid == Fluids.EMPTY;
  98. }
  99. @Override
  100. public EntryStack copy() {
  101. EntryStack stack = EntryStack.create(fluid, amount);
  102. for (Map.Entry<Settings<?>, Object> entry : getSettings().entrySet()) {
  103. stack.setting((Settings<? super Object>) entry.getKey(), entry.getValue());
  104. }
  105. return stack;
  106. }
  107. @Override
  108. public Object getObject() {
  109. return fluid;
  110. }
  111. @Override
  112. public boolean equalsIgnoreTagsAndAmount(EntryStack stack) {
  113. if (stack.getType() != Type.FLUID)
  114. return false;
  115. return fluid == stack.getFluid();
  116. }
  117. @Override
  118. public boolean equalsIgnoreTags(EntryStack stack) {
  119. if (stack.getType() != Type.FLUID)
  120. return false;
  121. return fluid == stack.getFluid() && amount == stack.getAmount();
  122. }
  123. @Override
  124. public boolean equalsIgnoreAmount(EntryStack stack) {
  125. if (stack.getType() != Type.FLUID)
  126. return false;
  127. return fluid == stack.getFluid();
  128. }
  129. @Override
  130. public boolean equalsAll(EntryStack stack) {
  131. if (stack.getType() != Type.FLUID)
  132. return false;
  133. return fluid == stack.getFluid() && amount == stack.getAmount();
  134. }
  135. @Override
  136. public int hashOfAll() {
  137. int result = hashIgnoreAmountAndTags();
  138. result = 31 * result + amount;
  139. return result;
  140. }
  141. @Override
  142. public int hashIgnoreAmountAndTags() {
  143. int result = 1;
  144. result = 31 * result + getType().ordinal();
  145. result = 31 * result + fluid.hashCode();
  146. return result;
  147. }
  148. @Override
  149. public int hashIgnoreTags() {
  150. return hashOfAll();
  151. }
  152. @Override
  153. public int hashIgnoreAmount() {
  154. return hashIgnoreAmountAndTags();
  155. }
  156. @Nullable
  157. @Override
  158. public Tooltip getTooltip(Point point) {
  159. if (!get(Settings.TOOLTIP_ENABLED).get() || isEmpty())
  160. return null;
  161. List<String> toolTip = Lists.newArrayList(SearchArgument.tryGetEntryStackName(this));
  162. if (amount >= 0) {
  163. String amountTooltip = get(Settings.Fluid.AMOUNT_TOOLTIP).apply(this);
  164. if (amountTooltip != null)
  165. toolTip.addAll(Arrays.asList(amountTooltip.split("\n")));
  166. }
  167. toolTip.addAll(get(Settings.TOOLTIP_APPEND_EXTRA).apply(this));
  168. if (get(Settings.TOOLTIP_APPEND_MOD).get() && ConfigObject.getInstance().shouldAppendModNames()) {
  169. final String modString = ClientHelper.getInstance().getFormattedModFromIdentifier(Registry.FLUID.getId(fluid));
  170. boolean alreadyHasMod = false;
  171. for (String s : toolTip)
  172. if (s.equalsIgnoreCase(modString)) {
  173. alreadyHasMod = true;
  174. break;
  175. }
  176. if (!alreadyHasMod)
  177. toolTip.add(modString);
  178. }
  179. return Tooltip.create(toolTip);
  180. }
  181. @SuppressWarnings("deprecation")
  182. @Override
  183. public void render(Rectangle bounds, int mouseX, int mouseY, float delta) {
  184. if (get(Settings.RENDER).get()) {
  185. Pair<Sprite, Integer> pair = getOrLoadSprite(getFluid());
  186. if (pair != null) {
  187. Sprite sprite = pair.getLeft();
  188. int color = pair.getRight();
  189. int a = 255;
  190. int r = (color >> 16 & 255);
  191. int g = (color >> 8 & 255);
  192. int b = (color & 255);
  193. MinecraftClient.getInstance().getTextureManager().bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX);
  194. Tessellator tess = Tessellator.getInstance();
  195. BufferBuilder bb = tess.getBuffer();
  196. bb.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
  197. bb.vertex(bounds.getMaxX(), bounds.y, getZ()).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, a).next();
  198. bb.vertex(bounds.x, bounds.y, getZ()).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, a).next();
  199. bb.vertex(bounds.x, bounds.getMaxY(), getZ()).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, a).next();
  200. bb.vertex(bounds.getMaxX(), bounds.getMaxY(), getZ()).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, a).next();
  201. tess.draw();
  202. }
  203. }
  204. }
  205. }