]> git.lizzy.rs Git - LightOverlay.git/blob - src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java
v4.3
[LightOverlay.git] / src / main / java / me / shedaniel / lightoverlay / LightOverlayClient.java
1 package me.shedaniel.lightoverlay;
2
3 import com.mojang.blaze3d.systems.RenderSystem;
4 import net.minecraft.block.Block;
5 import net.minecraft.block.BlockState;
6 import net.minecraft.client.Minecraft;
7 import net.minecraft.client.entity.player.ClientPlayerEntity;
8 import net.minecraft.client.gui.FontRenderer;
9 import net.minecraft.client.renderer.*;
10 import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
11 import net.minecraft.client.settings.KeyBinding;
12 import net.minecraft.client.util.InputMappings;
13 import net.minecraft.entity.Entity;
14 import net.minecraft.entity.EntityClassification;
15 import net.minecraft.entity.EntityType;
16 import net.minecraft.tags.BlockTags;
17 import net.minecraft.util.Direction;
18 import net.minecraft.util.ResourceLocation;
19 import net.minecraft.util.math.BlockPos;
20 import net.minecraft.util.math.shapes.ISelectionContext;
21 import net.minecraft.util.math.shapes.VoxelShape;
22 import net.minecraft.util.text.TranslationTextComponent;
23 import net.minecraft.world.LightType;
24 import net.minecraft.world.World;
25 import net.minecraft.world.biome.Biome;
26 import net.minecraftforge.api.distmarker.Dist;
27 import net.minecraftforge.client.event.InputEvent;
28 import net.minecraftforge.client.event.RenderWorldLastEvent;
29 import net.minecraftforge.client.settings.KeyConflictContext;
30 import net.minecraftforge.client.settings.KeyModifier;
31 import net.minecraftforge.common.MinecraftForge;
32 import net.minecraftforge.eventbus.api.SubscribeEvent;
33 import net.minecraftforge.fml.DistExecutor;
34 import net.minecraftforge.fml.client.registry.ClientRegistry;
35
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.FileOutputStream;
39 import java.io.IOException;
40 import java.text.DecimalFormat;
41 import java.util.Properties;
42
43 public class LightOverlayClient {
44     
45     static final DecimalFormat FORMAT = new DecimalFormat("#.#");
46     private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
47     private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
48     private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
49     private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
50     private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
51     private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
52     static int reach = 7;
53     static int crossLevel = 7;
54     static boolean showNumber = false;
55     static EntityType<Entity> testingEntityType;
56     static float lineWidth = 1.0F;
57     static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
58     static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
59     private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
60     private static boolean enabled = false;
61     
62     public static void register() {
63         // Load Config
64         loadConfig(configFile);
65         
66         // Setup
67         testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
68         enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
69         increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
70         decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
71         increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
72         decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
73         MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
74         
75         try {
76             //noinspection Convert2MethodRef
77             DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
78         } catch (Exception e) {
79             e.printStackTrace();
80         }
81     }
82     
83     public static CrossType getCrossType(BlockPos pos, BlockPos down, World world, ISelectionContext playerEntity) {
84         BlockState blockBelowState = world.getBlockState(down);
85         BlockState blockUpperState = world.getBlockState(pos);
86         VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, playerEntity);
87         if (!blockUpperState.getFluidState().isEmpty())
88             return CrossType.NONE;
89         /* WorldEntitySpawner.func_222266_a */
90         // Check if the outline is full
91         if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
92             return CrossType.NONE;
93         // Check if there is power
94         if (blockUpperState.canProvidePower())
95             return CrossType.NONE;
96         // Check if the collision has a bump
97         if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
98             return CrossType.NONE;
99         if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
100             return CrossType.NONE;
101         // Check block state allow spawning (excludes bedrock and barriers automatically)
102         if (!blockBelowState.canEntitySpawn(world, down, testingEntityType))
103             return CrossType.NONE;
104         if (world.func_226658_a_(LightType.BLOCK, pos) > crossLevel)
105             return CrossType.NONE;
106         if (world.func_226658_a_(LightType.SKY, pos) > crossLevel)
107             return CrossType.YELLOW;
108         return CrossType.RED;
109     }
110     
111     public static int getCrossLevel(BlockPos pos, BlockPos down, World world, ISelectionContext context) {
112         BlockState blockBelowState = world.getBlockState(down);
113         BlockState blockUpperState = world.getBlockState(pos);
114         VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, context);
115         VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, context);
116         if (!blockUpperState.getFluidState().isEmpty())
117             return -1;
118         if (!blockBelowState.getFluidState().isEmpty())
119             return -1;
120         if (blockBelowState.isAir(world, down))
121             return -1;
122         if (!blockUpperState.isAir(world, pos))
123             return -1;
124         return world.func_226658_a_(LightType.BLOCK, pos);
125     }
126     
127     public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
128         double d0 = info.getProjectedView().x;
129         double d1 = info.getProjectedView().y - .005D;
130         VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
131         if (!upperOutlineShape.isEmpty())
132             d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
133         double d2 = info.getProjectedView().z;
134         buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
135         int red = (color >> 16) & 255;
136         int green = (color >> 8) & 255;
137         int blue = color & 255;
138         buffer.func_225582_a_(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).func_225586_a_(red, green, blue, 255).endVertex();
139         buffer.func_225582_a_(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).func_225586_a_(red, green, blue, 255).endVertex();
140         buffer.func_225582_a_(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).func_225586_a_(red, green, blue, 255).endVertex();
141         buffer.func_225582_a_(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).func_225586_a_(red, green, blue, 255).endVertex();
142         tessellator.draw();
143     }
144     
145     public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
146         String string_1 = String.valueOf(level);
147         FontRenderer fontRenderer = minecraft.fontRenderer;
148         double double_4 = info.getProjectedView().x;
149         double double_5 = info.getProjectedView().y;
150         VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
151         if (!upperOutlineShape.isEmpty())
152             double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
153         double double_6 = info.getProjectedView().z;
154         RenderSystem.pushMatrix();
155         RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
156         RenderSystem.rotatef(90, 1, 0, 0);
157         RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
158         float size = 0.07F;
159         RenderSystem.scalef(-size, -size, size);
160         float float_3 = (float) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
161         RenderSystem.enableAlphaTest();
162         IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.func_228455_a_(Tessellator.getInstance().getBuffer());
163         fontRenderer.func_228079_a_(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.func_227983_a_().func_227988_c_(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
164         vertexConsumerProvider$Immediate_1.func_228461_a_();
165         RenderSystem.popMatrix();
166     }
167     
168     @SubscribeEvent(receiveCanceled = true)
169     public static void handleInput(InputEvent.KeyInputEvent event) {
170         if (enableOverlay.isPressed())
171             enabled = !enabled;
172         if (increaseReach.isPressed()) {
173             if (reach < 50)
174                 reach++;
175             try {
176                 saveConfig(configFile);
177             } catch (IOException e) {
178                 e.printStackTrace();
179             }
180             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
181         }
182         if (decreaseReach.isPressed()) {
183             if (reach > 1)
184                 reach--;
185             try {
186                 saveConfig(configFile);
187             } catch (IOException e) {
188                 e.printStackTrace();
189             }
190             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
191         }
192         if (increaseLineWidth.isPressed()) {
193             if (lineWidth < 7)
194                 lineWidth += 0.1f;
195             try {
196                 saveConfig(configFile);
197             } catch (IOException e) {
198                 e.printStackTrace();
199             }
200             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
201         }
202         if (decreaseLineWidth.isPressed()) {
203             if (lineWidth > 1)
204                 lineWidth -= 0.1F;
205             try {
206                 saveConfig(configFile);
207             } catch (IOException e) {
208                 e.printStackTrace();
209             }
210             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
211         }
212     }
213     
214     @SubscribeEvent
215     public static void renderWorldLast(RenderWorldLastEvent event) {
216         if (LightOverlayClient.enabled) {
217             RenderSystem.pushMatrix();
218             RenderSystem.loadIdentity();
219             RenderSystem.multMatrix(event.getMatrixStack().func_227866_c_().func_227870_a_());
220             Minecraft client = Minecraft.getInstance();
221             ClientPlayerEntity playerEntity = client.player;
222             ISelectionContext context = ISelectionContext.forEntity(playerEntity);
223             World world = client.world;
224             BlockPos playerPos = playerEntity.getPosition();
225             ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
226             if (showNumber) {
227                 RenderSystem.enableTexture();
228                 RenderSystem.enableDepthTest();
229                 
230                 RenderSystem.depthMask(true);
231                 for (BlockPos pos : BlockPos.getAllInBoxMutable(playerPos.add(-reach, -reach, -reach), playerPos.add(reach, reach, reach))) {
232                     Biome biome = world.func_226691_t_(pos);
233                     if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
234                         BlockPos down = pos.down();
235                         int level = LightOverlayClient.getCrossLevel(pos, down, world, context);
236                         if (level >= 0) {
237                             LightOverlayClient.renderLevel(client, info, world, pos, down, level, context);
238                         }
239                     }
240                 }
241                 RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
242                 RenderSystem.enableDepthTest();
243             } else {
244                 RenderSystem.disableTexture();
245                 RenderSystem.disableBlend();
246                 RenderSystem.lineWidth(lineWidth);
247                 RenderSystem.depthMask(false);
248                 Tessellator tessellator = Tessellator.getInstance();
249                 BufferBuilder buffer = tessellator.getBuffer();
250                 for (BlockPos pos : BlockPos.getAllInBoxMutable(playerPos.add(-reach, -reach, -reach), playerPos.add(reach, reach, reach))) {
251                     Biome biome = world.func_226691_t_(pos);
252                     if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
253                         BlockPos down = pos.down();
254                         CrossType type = LightOverlayClient.getCrossType(pos, down, world, context);
255                         if (type != CrossType.NONE) {
256                             int color = type == CrossType.RED ? redColor : yellowColor;
257                             LightOverlayClient.renderCross(info, tessellator, buffer, world, pos, color, context);
258                         }
259                     }
260                 }
261                 RenderSystem.depthMask(true);
262             }
263             RenderSystem.popMatrix();
264         }
265     }
266     
267     private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
268         KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
269         ClientRegistry.registerKeyBinding(keyBinding);
270         return keyBinding;
271     }
272     
273     static void loadConfig(File file) {
274         try {
275             redColor = 0xFF0000;
276             yellowColor = 0xFFFF00;
277             if (!file.exists() || !file.canRead())
278                 saveConfig(file);
279             FileInputStream fis = new FileInputStream(file);
280             Properties properties = new Properties();
281             properties.load(fis);
282             fis.close();
283             reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "7"));
284             crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
285             showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
286             lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
287             {
288                 int r, g, b;
289                 r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
290                 g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
291                 b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
292                 yellowColor = (r << 16) + (g << 8) + b;
293             }
294             {
295                 int r, g, b;
296                 r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
297                 g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
298                 b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
299                 redColor = (r << 16) + (g << 8) + b;
300             }
301             saveConfig(file);
302         } catch (Exception e) {
303             e.printStackTrace();
304             reach = 7;
305             lineWidth = 1.0F;
306             redColor = 0xFF0000;
307             yellowColor = 0xFFFF00;
308             try {
309                 saveConfig(file);
310             } catch (IOException ex) {
311                 ex.printStackTrace();
312             }
313         }
314     }
315     
316     static void saveConfig(File file) throws IOException {
317         FileOutputStream fos = new FileOutputStream(file, false);
318         fos.write("# Light Overlay Config".getBytes());
319         fos.write("\n".getBytes());
320         fos.write(("reach=" + reach).getBytes());
321         fos.write("\n".getBytes());
322         fos.write(("crossLevel=" + crossLevel).getBytes());
323         fos.write("\n".getBytes());
324         fos.write(("showNumber=" + showNumber).getBytes());
325         fos.write("\n".getBytes());
326         fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
327         fos.write("\n".getBytes());
328         fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
329         fos.write("\n".getBytes());
330         fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
331         fos.write("\n".getBytes());
332         fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
333         fos.write("\n".getBytes());
334         fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
335         fos.write("\n".getBytes());
336         fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
337         fos.write("\n".getBytes());
338         fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
339         fos.close();
340     }
341     
342     private enum CrossType {
343         YELLOW,
344         RED,
345         NONE
346     }
347     
348 }