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