]> git.lizzy.rs Git - LightOverlay.git/blob - src/main/java/me/shedaniel/lightoverlay/LightOverlay.java
c984b96e364384df85a1fe06f8b27bb3cbbc96f4
[LightOverlay.git] / src / main / java / me / shedaniel / lightoverlay / LightOverlay.java
1 package me.shedaniel.lightoverlay;
2
3 import com.mojang.blaze3d.platform.GlStateManager;
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.renderer.ActiveRenderInfo;
9 import net.minecraft.client.renderer.BufferBuilder;
10 import net.minecraft.client.renderer.Tessellator;
11 import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
12 import net.minecraft.client.settings.KeyBinding;
13 import net.minecraft.client.util.InputMappings;
14 import net.minecraft.entity.Entity;
15 import net.minecraft.entity.EntityClassification;
16 import net.minecraft.entity.EntityType;
17 import net.minecraft.entity.player.PlayerEntity;
18 import net.minecraft.tags.BlockTags;
19 import net.minecraft.util.Direction;
20 import net.minecraft.util.ResourceLocation;
21 import net.minecraft.util.math.BlockPos;
22 import net.minecraft.util.math.shapes.ISelectionContext;
23 import net.minecraft.util.math.shapes.VoxelShape;
24 import net.minecraft.util.text.TranslationTextComponent;
25 import net.minecraft.world.LightType;
26 import net.minecraft.world.World;
27 import net.minecraft.world.biome.Biome;
28 import net.minecraftforge.api.distmarker.Dist;
29 import net.minecraftforge.api.distmarker.OnlyIn;
30 import net.minecraftforge.client.event.InputEvent;
31 import net.minecraftforge.client.event.RenderWorldLastEvent;
32 import net.minecraftforge.client.settings.KeyConflictContext;
33 import net.minecraftforge.client.settings.KeyModifier;
34 import net.minecraftforge.common.MinecraftForge;
35 import net.minecraftforge.eventbus.api.SubscribeEvent;
36 import net.minecraftforge.fml.client.registry.ClientRegistry;
37 import net.minecraftforge.fml.common.Mod;
38
39 import java.awt.*;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
44 import java.text.DecimalFormat;
45 import java.util.Map;
46 import java.util.Properties;
47
48 @OnlyIn(Dist.CLIENT)
49 @Mod("lightoverlay-forge")
50 public class LightOverlay {
51     
52     private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
53     private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
54     private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
55     private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
56     private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
57     private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
58     private static final DecimalFormat FORMAT = new DecimalFormat("#.#");
59     private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
60     private static boolean enabled = false;
61     private static int reach = 12;
62     private static EntityType<Entity> testingEntityType;
63     private static float lineWidth = 1.0F;
64     private static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
65     
66     public LightOverlay() {
67         // Load Config
68         loadConfig(configFile);
69         
70         // Setup
71         testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
72         enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
73         increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
74         decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
75         increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
76         decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
77         MinecraftForge.EVENT_BUS.register(this);
78     }
79     
80     public static CrossType getCrossType(BlockPos pos, World world, PlayerEntity playerEntity) {
81         BlockState blockBelowState = world.getBlockState(pos.down());
82         BlockState blockUpperState = world.getBlockState(pos);
83         VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, ISelectionContext.forEntity(playerEntity));
84         if (!blockUpperState.getFluidState().isEmpty())
85             return CrossType.NONE;
86         /* WorldEntitySpawner.func_222266_a */
87         // Check if the outline is full
88         if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
89             return CrossType.NONE;
90         // Check if there is power
91         if (blockUpperState.canProvidePower())
92             return CrossType.NONE;
93         // Check if the collision has a bump
94         if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
95             return CrossType.NONE;
96         if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
97             return CrossType.NONE;
98         // Check block state allow spawning (excludes bedrock and barriers automatically)
99         if (!blockBelowState.canEntitySpawn(world, pos.down(), testingEntityType))
100             return CrossType.NONE;
101         if (world.getLightFor(LightType.BLOCK, pos) >= 8)
102             return CrossType.NONE;
103         if (world.getLightFor(LightType.SKY, pos) >= 8)
104             return CrossType.YELLOW;
105         return CrossType.RED;
106     }
107     
108     public static void renderCross(World world, BlockPos pos, Color color, PlayerEntity entity) {
109         ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
110         GlStateManager.lineWidth(lineWidth);
111         GlStateManager.depthMask(false);
112         GlStateManager.disableTexture();
113         Tessellator tessellator = Tessellator.getInstance();
114         BufferBuilder buffer = tessellator.getBuffer();
115         double d0 = info.getProjectedView().x;
116         double d1 = info.getProjectedView().y - .005D;
117         VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, ISelectionContext.forEntity(entity));
118         if (!upperOutlineShape.isEmpty())
119             d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
120         double d2 = info.getProjectedView().z;
121         
122         buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
123         buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()).endVertex();
124         buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()).endVertex();
125         buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()).endVertex();
126         buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()).endVertex();
127         tessellator.draw();
128         GlStateManager.depthMask(true);
129         GlStateManager.enableTexture();
130     }
131     
132     @SubscribeEvent(receiveCanceled = true)
133     public void handleInput(InputEvent.KeyInputEvent event) {
134         if (enableOverlay.isPressed())
135             enabled = !enabled;
136         if (increaseReach.isPressed()) {
137             if (reach < 50)
138                 reach++;
139             try {
140                 saveConfig(configFile);
141             } catch (IOException e) {
142                 e.printStackTrace();
143             }
144             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
145         }
146         if (decreaseReach.isPressed()) {
147             if (reach > 1)
148                 reach--;
149             try {
150                 saveConfig(configFile);
151             } catch (IOException e) {
152                 e.printStackTrace();
153             }
154             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
155         }
156         if (increaseLineWidth.isPressed()) {
157             if (lineWidth < 7)
158                 lineWidth += 0.1f;
159             try {
160                 saveConfig(configFile);
161             } catch (IOException e) {
162                 e.printStackTrace();
163             }
164             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
165         }
166         if (decreaseLineWidth.isPressed()) {
167             if (lineWidth > 1)
168                 lineWidth -= 0.1F;
169             try {
170                 saveConfig(configFile);
171             } catch (IOException e) {
172                 e.printStackTrace();
173             }
174             Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
175         }
176     }
177     
178     @SubscribeEvent
179     public void renderWorldLast(RenderWorldLastEvent event) {
180         if (LightOverlay.enabled) {
181             Minecraft client = Minecraft.getInstance();
182             ClientPlayerEntity playerEntity = client.player;
183             World world = client.world;
184             GlStateManager.disableTexture();
185             GlStateManager.disableBlend();
186             BlockPos playerPos = new BlockPos(playerEntity.posX, playerEntity.posY, playerEntity.posZ);
187             BlockPos.getAllInBox(playerPos.add(-reach, -reach, -reach), playerPos.add(reach, reach, reach)).forEach(pos -> {
188                 Biome biome = world.getBiome(pos);
189                 if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
190                     CrossType type = LightOverlay.getCrossType(pos, world, playerEntity);
191                     if (type != CrossType.NONE) {
192                         VoxelShape shape = world.getBlockState(pos).getCollisionShape(world, pos);
193                         Color color = type == CrossType.RED ? Color.RED : Color.YELLOW;
194                         LightOverlay.renderCross(world, pos, color, playerEntity);
195                     }
196                 }
197             });
198             GlStateManager.enableBlend();
199             GlStateManager.enableTexture();
200         }
201     }
202     
203     private KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
204         KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
205         ClientRegistry.registerKeyBinding(keyBinding);
206         return keyBinding;
207     }
208     
209     private void loadConfig(File file) {
210         try {
211             if (!file.exists() || !file.canRead())
212                 saveConfig(file);
213             FileInputStream fis = new FileInputStream(file);
214             Properties properties = new Properties();
215             properties.load(fis);
216             fis.close();
217             for(Map.Entry<Object, Object> entry : properties.entrySet()) {
218                 if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
219                     String key = (String) entry.getKey();
220                     String value = (String) entry.getValue();
221                     if (key.equals("reach")) {
222                         try {
223                             reach = Integer.valueOf(value);
224                         } catch (NumberFormatException e) {
225                             e.printStackTrace();
226                             reach = 12;
227                         }
228                     } else if (key.equals("lineWidth")) {
229                         try {
230                             lineWidth = Float.valueOf(value);
231                         } catch (NumberFormatException e) {
232                             e.printStackTrace();
233                             lineWidth = 1.0F;
234                         }
235                     }
236                 }
237             }
238             saveConfig(file);
239         } catch (Exception e) {
240             e.printStackTrace();
241             reach = 12;
242             lineWidth = 1.0F;
243         }
244     }
245     
246     private void saveConfig(File file) throws IOException {
247         FileOutputStream fos = new FileOutputStream(file, false);
248         fos.write(("reach=" + String.valueOf(reach)).getBytes());
249         fos.write("\n".getBytes());
250         fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
251         fos.close();
252     }
253     
254     private static enum CrossType {
255         YELLOW,
256         RED,
257         NONE
258     }
259     
260 }