diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..142f2a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +build/ +*.ipr +run/ +*.iws +out/ +*.iml +.gradle/ +output/ +bin/ +libs/ + +.classpath +.project +.idea/ +classes/ +.metadata +.vscode +.settings +*.launch + +dist/ \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..2326983 --- /dev/null +++ b/build.gradle @@ -0,0 +1,59 @@ +plugins { + id "architectury-plugin" version "3.1-SNAPSHOT" + id "forgified-fabric-loom" version "0.6-SNAPSHOT" apply false +} + +architectury { + minecraft = rootProject.minecraft_version +} + +subprojects { + apply plugin: "forgified-fabric-loom" + + loom { + silentMojangMappingsLicense() + mixinConfig "mixin.civmodern.json" + } + + /*dependencies { + minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" + // The following line declares the mojmap mappings, you may use other mappings as well + mappings loom.officialMojangMappings() + // The following line declares the yarn mappings you may select this one as well. +// mappings "net.fabricmc:yarn:1.16.1+build.21" + }*/ +} + +allprojects { + apply plugin: "java" + apply plugin: "architectury-plugin" + apply plugin: "maven-publish" + + archivesBaseName = rootProject.archives_base_name + version = rootProject.mod_version + group = rootProject.maven_group + + repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. + } + + tasks.withType(JavaCompile) { + options.encoding = "UTF-8" + + // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too + // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. + // We'll use that if it's available, but otherwise we'll use the older option. + def targetVersion = 8 + if (JavaVersion.current().isJava9Compatible()) { + options.release = targetVersion + } + } + + java { + withSourcesJar() + } +} diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..2c64a73 --- /dev/null +++ b/changelog.md @@ -0,0 +1,11 @@ +# 1.1 + +- Add toggle to player pings +- Minor bugfixes for hold left and right macros +- Add option to rollback colours to presets +- HSV colour picker starts with the current hue automatically selected +- Add ice road macro (default key is backspace, can optionally snap to a cardinal or intercardinal direction) + +# 1.0.1 + +- Fix crash bug on 1.16.1 \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..a464bf0 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,37 @@ +dependencies { + // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies + // Do NOT use other classes from fabric loader + modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + // Remove the next line if you don't want to depend on the API + //modApi "me.shedaniel:architectury:${rootProject.architectury_version}" + + minecraft "com.mojang:minecraft:1.16.5" + mappings loom.officialMojangMappings() +} + +architectury { + injectInjectables = false + common() +} + +java { + withSourcesJar() +} + +publishing { + publications { + mavenCommon(MavenPublication) { + artifactId = rootProject.archives_base_name + // add all the jars that should be included when publishing to maven + artifact remapJar + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/AbstractCivModernMod.java b/common/src/main/java/sh/okx/civmodern/common/AbstractCivModernMod.java new file mode 100644 index 0000000..f3903a0 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/AbstractCivModernMod.java @@ -0,0 +1,206 @@ +package sh.okx.civmodern.common; + +import com.mojang.blaze3d.platform.InputConstants.Type; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.Properties; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.core.NonNullList; +import net.minecraft.core.Registry; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.system.CallbackI.I; +import sh.okx.civmodern.common.compat.CompatProvider; +import sh.okx.civmodern.common.compat.v1_16_1.v1_16_1CompatProvider; +import sh.okx.civmodern.common.compat.v1_16_5.v1_16_5CompatProvider; +import sh.okx.civmodern.common.events.ClientTickEvent; +import sh.okx.civmodern.common.events.EventBus; +import sh.okx.civmodern.common.gui.screen.MainConfigScreen; +import sh.okx.civmodern.common.macro.HoldKeyMacro; +import sh.okx.civmodern.common.macro.IceRoadMacro; +import sh.okx.civmodern.common.radar.Radar; + +public abstract class AbstractCivModernMod { + + private static AbstractCivModernMod INSTANCE; + private static final Logger LOGGER = LogManager.getLogger(); + + private final KeyMapping configBinding; + private final KeyMapping holdLeftBinding; + private final KeyMapping holdRightBinding; + private final KeyMapping iceRoadBinding; + private final CompatProvider compat; + private CivMapConfig config; + private Radar radar; + + private HoldKeyMacro leftMacro; + private HoldKeyMacro rightMacro; + private IceRoadMacro iceRoadMacro; + + private EventBus eventBus; + + public AbstractCivModernMod() { + int version = Minecraft.getInstance().getGame().getVersion().getProtocolVersion(); + if (version == 754) { + this.compat = new v1_16_5CompatProvider(); + } else { + this.compat = new v1_16_1CompatProvider(); + } + + this.configBinding = new KeyMapping( + "key.civmodern.config", + Type.KEYSYM, + GLFW.GLFW_KEY_R, + "category.civmodern" + ); + this.holdLeftBinding = new KeyMapping( + "key.civmodern.left", + Type.KEYSYM, + GLFW.GLFW_KEY_MINUS, + "category.civmodern" + ); + this.holdRightBinding = new KeyMapping( + "key.civmodern.right", + Type.KEYSYM, + GLFW.GLFW_KEY_EQUAL, + "category.civmodern" + ); + this.iceRoadBinding = new KeyMapping( + "key.civmodern.ice", + Type.KEYSYM, + GLFW.GLFW_KEY_BACKSPACE, + "category.civmodern" + ); + + if (INSTANCE == null) { + INSTANCE = this; + } else { + throw new IllegalStateException("AbstractCivModernMod initialised twice"); + } + } + + public final void enable() { + this.eventBus = provideEventBus(); + + registerKeyBinding(this.configBinding); + registerKeyBinding(this.holdLeftBinding); + registerKeyBinding(this.holdRightBinding); + registerKeyBinding(this.iceRoadBinding); + + loadConfig(); + replaceItemRenderer(); + + this.eventBus.listen(ClientTickEvent.class, e -> this.tick()); + + Options options = Minecraft.getInstance().options; + this.leftMacro = new HoldKeyMacro(this, this.holdLeftBinding, options.keyAttack); + this.rightMacro = new HoldKeyMacro(this, this.holdRightBinding, options.keyUse); + this.iceRoadMacro = new IceRoadMacro(this, config, this.iceRoadBinding); + } + + public abstract EventBus provideEventBus(); + public abstract void registerKeyBinding(KeyMapping mapping); + + private void onScroll() { + if (this.leftMacro != null) this.leftMacro.onScroll(); + if (this.rightMacro != null) this.rightMacro.onScroll(); + } + + private void tick() { + while (configBinding.consumeClick()) { + Minecraft.getInstance().setScreen(new MainConfigScreen(this, config)); + } + } + + private void replaceItemRenderer() { + // Look man, it's this or mixins + Minecraft minecraft = Minecraft.getInstance(); + for (Field field : Minecraft.class.getDeclaredFields()) { + if (field.getType() == ItemRenderer.class) { + field.setAccessible(true); + try { + field.set(minecraft, new CustomItemRenderer(minecraft.getItemRenderer(), config)); + replaceGuiItemRenderer(); + return; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + LOGGER.warn("Unable to replace item renderer"); + } + + private void replaceGuiItemRenderer() { + Minecraft minecraft = Minecraft.getInstance(); + Gui gui = minecraft.gui; + for (Field field : Gui.class.getDeclaredFields()) { + if (field.getType() == ItemRenderer.class) { + field.setAccessible(true); + try { + field.set(gui, minecraft.getItemRenderer()); + return; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + LOGGER.warn("Unable to replace hotbar item renderer"); + } + + private void loadConfig() { + Properties properties = new Properties(); + Path gameDir = Minecraft.getInstance().gameDirectory.toPath(); + File configFile = gameDir.resolve("config").resolve("civmodern.properties").toFile(); + try { + if (!configFile.exists()) { + InputStream resource = AbstractCivModernMod.class + .getResourceAsStream("/civmodern.properties"); + byte[] buffer = new byte[resource.available()]; + resource.read(buffer); + FileOutputStream fos = new FileOutputStream(configFile); + fos.write(buffer); + } + + FileInputStream input = new FileInputStream(configFile); + properties.load(input); + } catch (IOException ex) { + ex.printStackTrace(); + } + CivMapConfig config = new CivMapConfig(configFile, properties); + + this.config = config; + + this.radar = new Radar(config, eventBus, compat); + this.radar.init(); + } + + public EventBus getEventBus() { + return eventBus; + } + + public CompatProvider getCompat() { + return compat; + } + + public static void staticOnScroll() { + if (INSTANCE != null) { + INSTANCE.onScroll(); + } + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/CivMapConfig.java b/common/src/main/java/sh/okx/civmodern/common/CivMapConfig.java new file mode 100644 index 0000000..d42bb60 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/CivMapConfig.java @@ -0,0 +1,171 @@ +package sh.okx.civmodern.common; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import sh.okx.civmodern.common.gui.Alignment; + +public class CivMapConfig { + private final File file; + private int compactedColour; + private int radarCircles; + private int radarSize; + private float iconSize; + private Alignment alignment; + private double range; + private float transparency; + private int radarColour; + private boolean radarEnabled; + private boolean pingEnabled; + private int x; + private int y; + private boolean iceRoadCardinalEnabled; + + private String s = "hello".toUpperCase().toUpperCase(); + + public CivMapConfig(File file, Properties properties) { + this.file = file; + this.compactedColour = Integer.parseInt(properties.getProperty("compacted_colour", "16777048")); + this.radarCircles = Integer.parseInt(properties.getProperty("radar_circles", "4")); + this.radarSize = Integer.parseInt(properties.getProperty("radar_size", "50")); + this.alignment = Alignment.valueOf(properties.getProperty("alignment", "top_left").toUpperCase()); + this.iconSize = Float.parseFloat(properties.getProperty("icon_size", "1")); + this.range = Double.parseDouble(properties.getProperty("range", "64")); + this.transparency = Float.parseFloat(properties.getProperty("transparency", "0.8")); + this.radarColour = Integer.parseInt(properties.getProperty("radar_colour", "8947848")); + this.x = Integer.parseInt(properties.getProperty("x", "100")); + this.y = Integer.parseInt(properties.getProperty("y", "100")); + this.radarEnabled = Boolean.parseBoolean(properties.getProperty("radar_enabled", "true")); + this.pingEnabled = Boolean.parseBoolean(properties.getProperty("ping_enabled", "true")); + this.iceRoadCardinalEnabled = Boolean.parseBoolean(properties.getProperty("ice_road_cardinal", "true")); + } + + public void save() { + try { + Properties properties = new Properties(); + properties.setProperty("compacted_colour", Integer.toString(compactedColour)); + properties.setProperty("radar_circles", Integer.toString(radarCircles)); + properties.setProperty("radar_size", Integer.toString(radarSize)); + properties.setProperty("alignment", alignment.name().toLowerCase()); + properties.setProperty("icon_size", Float.toString(iconSize)); + properties.setProperty("range", Double.toString(range)); + properties.setProperty("transparency", Float.toString(transparency)); + properties.setProperty("radar_colour", Integer.toString(radarColour)); + properties.setProperty("x", Integer.toString(x)); + properties.setProperty("y", Integer.toString(y)); + properties.setProperty("radar_enabled", Boolean.toString(radarEnabled)); + properties.setProperty("ping_enabled", Boolean.toString(pingEnabled)); + properties.setProperty("ice_road_cardinal", Boolean.toString(iceRoadCardinalEnabled)); + + FileOutputStream output = new FileOutputStream(file); + properties.store(output, null); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public int getColour() { + return compactedColour; + } + + public void setColour(int compactedColour) { + this.compactedColour = compactedColour; + } + + public void setRadarCircles(int radarCircles) { + this.radarCircles = radarCircles; + } + + public int getRadarCircles() { + return radarCircles; + } + + public int getRadarSize() { + return radarSize; + } + + public void setRadarSize(int radarSize) { + this.radarSize = radarSize; + } + + public Alignment getAlignment() { + return alignment; + } + + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } + + public float getIconSize() { + return iconSize; + } + + public void setIconSize(float iconSize) { + this.iconSize = iconSize; + } + + public double getRange() { + return range; + } + + public void setRange(double range) { + this.range = range; + } + + public float getTransparency() { + return transparency; + } + + public void setTransparency(float transparency) { + this.transparency = transparency; + } + + public int getRadarColour() { + return radarColour; + } + + public void setRadarColour(int radarColour) { + this.radarColour = radarColour; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public boolean isRadarEnabled() { + return radarEnabled; + } + + public void setRadarEnabled(boolean radarEnabled) { + this.radarEnabled = radarEnabled; + } + + public boolean isPingEnabled() { + return pingEnabled; + } + + public void setPingEnabled(boolean pingEnabled) { + this.pingEnabled = pingEnabled; + } + + public void setIceRoadCardinalEnabled(boolean iceRoadCardinalEnabled) { + this.iceRoadCardinalEnabled = iceRoadCardinalEnabled; + } + + public boolean isIceRoadCardinalEnabled() { + return iceRoadCardinalEnabled; + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/CustomItemRenderer.java b/common/src/main/java/sh/okx/civmodern/common/CustomItemRenderer.java new file mode 100644 index 0000000..e94b4aa --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/CustomItemRenderer.java @@ -0,0 +1,125 @@ +package sh.okx.civmodern.common; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import java.lang.reflect.Field; +import net.minecraft.client.Minecraft; +import net.minecraft.client.color.item.ItemColors; +import net.minecraft.client.gui.Font; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.ItemModelShaper; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.util.Mth; +import net.minecraft.world.item.ItemStack; + +public class CustomItemRenderer extends ItemRenderer { + + private final CivMapConfig config; + + public CustomItemRenderer(ItemRenderer old, CivMapConfig config) throws IllegalAccessException { + // Placeholder values just so it doesn't NPE + super(Minecraft.getInstance().getTextureManager(), Minecraft.getInstance().getModelManager(), + null); + this.config = config; + + // Steal all the fields from the old item renderer + // This is necessary otherwise all the items render with the missing texture + System.out.println("OLD IR: " + old); + for (Field field : ItemRenderer.class.getDeclaredFields()) { + if (field.getType() == ItemModelShaper.class + || field.getType() == ItemColors.class) { + field.setAccessible(true); + field.set(this, field.get(old)); + } + } + } + + @Override + public void renderGuiItemDecorations(Font renderer, ItemStack stack, int x, int y, String label) { + if (!stack.isEmpty()) { + PoseStack matrixstack = new PoseStack(); + boolean compacted = isCompacted(stack); + if (stack.getCount() != 1 || label != null || compacted) { + matrixstack.translate(0.0D, 0.0D, this.blitOffset + 200.0F); + MultiBufferSource.BufferSource irendertypebuffer$impl = MultiBufferSource.immediate( + Tesselator.getInstance().getBuilder()); + + //int colour = compacted ? 0xffff58 : 0xffffff; + int colour = compacted ? config.getColour() : 0xffffff; + String s = label == null ? String.valueOf(stack.getCount()) : label; + + renderer.drawInBatch(s, (float) (x + 19 - 2 - renderer.width(s)), (float) (y + 6 + 3), + colour, true, matrixstack.last().pose(), irendertypebuffer$impl, false, 0, 15728880); + irendertypebuffer$impl.endBatch(); + } + + if (stack.isDamaged()) { + RenderSystem.disableDepthTest(); + RenderSystem.disableTexture(); + RenderSystem.disableAlphaTest(); + RenderSystem.disableBlend(); + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferBuilder = tesselator.getBuilder(); + float f = (float)stack.getDamageValue(); + float g = (float)stack.getMaxDamage(); + float h = Math.max(0.0F, (g - f) / g); + int k = Math.round(13.0F - f * 13.0F / g); + int l = Mth.hsvToRgb(h / 3.0F, 1.0F, 1.0F); + this.fillRect(bufferBuilder, x + 2, y + 13, 13, 2, 0, 0, 0, 255); + this.fillRect(bufferBuilder, x + 2, y + 13, k, 1, l >> 16 & 255, l >> 8 & 255, l & 255, 255); + RenderSystem.enableBlend(); + RenderSystem.enableAlphaTest(); + RenderSystem.enableTexture(); + RenderSystem.enableDepthTest(); + } + + LocalPlayer localPlayer = Minecraft.getInstance().player; + float m = localPlayer == null ? 0.0F : localPlayer.getCooldowns().getCooldownPercent(stack.getItem(), Minecraft.getInstance().getFrameTime()); + if (m > 0.0F) { + RenderSystem.disableDepthTest(); + RenderSystem.disableTexture(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + Tesselator tesselator2 = Tesselator.getInstance(); + BufferBuilder bufferBuilder2 = tesselator2.getBuilder(); + this.fillRect(bufferBuilder2, x, y + Mth.floor(16.0F * (1.0F - m)), 16, Mth.ceil(16.0F * m), 255, 255, 255, 127); + RenderSystem.enableTexture(); + RenderSystem.enableDepthTest(); + } + } + } + + private void fillRect(BufferBuilder buffer, int a, int b, int c, int d, int e, int f, int g, + int h) { + buffer.begin(7, DefaultVertexFormat.POSITION_COLOR); + buffer.vertex(a, b, 0.0D).color(e, f, g, h).endVertex(); + buffer.vertex(a, b + d, 0.0D).color(e, f, g, h).endVertex(); + buffer.vertex(a + c, b + d, 0.0D).color(e, f, g, h).endVertex(); + buffer.vertex(a + c, b, 0.0D).color(e, f, g, h).endVertex(); + Tesselator.getInstance().end(); + } + + private boolean isCompacted(ItemStack item) { + if (!item.hasTag()) { + return false; + } + CompoundTag displayTag = item.getTagElement("display"); + if (displayTag != null && displayTag.getTagType("Lore") == 9) { + ListTag listTag = displayTag.getList("Lore", 8); + + for (int i = 0; i < listTag.size(); i++) { + String lore = listTag.getString(i); + if (lore.contains("Compacted Item")) { + return true; + } + } + } + return false; + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/CommonFont.java b/common/src/main/java/sh/okx/civmodern/common/compat/CommonFont.java new file mode 100644 index 0000000..bd064e2 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/CommonFont.java @@ -0,0 +1,14 @@ +package sh.okx.civmodern.common.compat; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; + +/** + * For some reason the Font class changed some method signatures between 1.16.1 and 1.16.5 + */ +public interface CommonFont { + void draw(PoseStack poseStack, Component component, float x, float y, int colour); + void drawShadow(PoseStack poseStack, Component component, float x, float y, int colour); + void drawShadowCentred(PoseStack poseStack, Component component, float x, float y, int colour); +} diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/CompatProvider.java b/common/src/main/java/sh/okx/civmodern/common/compat/CompatProvider.java new file mode 100644 index 0000000..6518641 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/CompatProvider.java @@ -0,0 +1,7 @@ +package sh.okx.civmodern.common.compat; + +import net.minecraft.client.gui.Font; + +public interface CompatProvider { + CommonFont provideFont(Font font); +} diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CommonFont.java b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CommonFont.java new file mode 100644 index 0000000..e6ac18f --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CommonFont.java @@ -0,0 +1,58 @@ +package sh.okx.civmodern.common.compat.v1_16_1; + +import com.mojang.blaze3d.vertex.PoseStack; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import net.minecraft.client.gui.Font; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import sh.okx.civmodern.common.compat.CommonFont; + +public class v1_16_1CommonFont implements CommonFont { + + private static final Logger LOGGER = LogManager.getLogger(); + + private final Font font; + private Method drawInternalMethod; + + public v1_16_1CommonFont(Font font) { + this.font = font; + + Class[] parameterTypes = {FormattedText.class, float.class, float.class, int.class, com.mojang.math.Matrix4f.class, boolean.class}; + for (Method method : font.getClass().getDeclaredMethods()) { + LOGGER.info(method); + if (Arrays.equals(method.getParameterTypes(), parameterTypes)) { + method.setAccessible(true); + this.drawInternalMethod = method; + LOGGER.info("Font.drawInternal: " + drawInternalMethod); + break; + } + } + } + + private void drawInternal(PoseStack stack, Component component, float x, float y, int colour, boolean shadow) { + try { + drawInternalMethod.invoke(font, component, x, y, colour, stack.last().pose(), shadow); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + @Override + public void draw(PoseStack poseStack, Component component, float x, float y, int colour) { + drawInternal(poseStack, component, x, y, colour, false); + } + + @Override + public void drawShadow(PoseStack poseStack, Component component, float x, float y, int colour) { + drawInternal(poseStack, component, x, y, colour, true); + } + + @Override + public void drawShadowCentred(PoseStack poseStack, Component component, float x, float y, int colour) { + drawShadow(poseStack, component, x - font.width(component) / 2f, y, colour); + } +} \ No newline at end of file diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CompatProvider.java b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CompatProvider.java new file mode 100644 index 0000000..c50a309 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_1/v1_16_1CompatProvider.java @@ -0,0 +1,13 @@ +package sh.okx.civmodern.common.compat.v1_16_1; + +import net.minecraft.client.gui.Font; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.compat.CompatProvider; + +public class v1_16_1CompatProvider implements CompatProvider { + + @Override + public CommonFont provideFont(Font font) { + return new v1_16_1CommonFont(font); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CommonFont.java b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CommonFont.java new file mode 100644 index 0000000..33c54e6 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CommonFont.java @@ -0,0 +1,31 @@ +package sh.okx.civmodern.common.compat.v1_16_5; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.Font; +import net.minecraft.network.chat.Component; +import sh.okx.civmodern.common.compat.CommonFont; + +public class v1_16_5CommonFont implements CommonFont { + + private final Font font; + + public v1_16_5CommonFont(Font font) { + this.font = font; + } + + @Override + public void draw(PoseStack poseStack, Component component, float x, float y, int colour) { + font.draw(poseStack, component, x, y, colour); + } + + @Override + public void drawShadow(PoseStack poseStack, Component component, float x, float y, int colour) { + font.drawShadow(poseStack, component, x, y, colour); + } + + @Override + public void drawShadowCentred(PoseStack poseStack, Component component, float x, float y, + int colour) { + font.drawShadow(poseStack, component, x - font.width(component) / 2f, y, colour); + } +} \ No newline at end of file diff --git a/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CompatProvider.java b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CompatProvider.java new file mode 100644 index 0000000..beeeff1 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/compat/v1_16_5/v1_16_5CompatProvider.java @@ -0,0 +1,13 @@ +package sh.okx.civmodern.common.compat.v1_16_5; + +import net.minecraft.client.gui.Font; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.compat.CompatProvider; + +public class v1_16_5CompatProvider implements CompatProvider { + + @Override + public CommonFont provideFont(Font font) { + return new v1_16_5CommonFont(font); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/events/ClientTickEvent.java b/common/src/main/java/sh/okx/civmodern/common/events/ClientTickEvent.java new file mode 100644 index 0000000..02d0b2f --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/events/ClientTickEvent.java @@ -0,0 +1,5 @@ +package sh.okx.civmodern.common.events; + +public class ClientTickEvent implements Event { + +} diff --git a/common/src/main/java/sh/okx/civmodern/common/events/Event.java b/common/src/main/java/sh/okx/civmodern/common/events/Event.java new file mode 100644 index 0000000..00787ec --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/events/Event.java @@ -0,0 +1,5 @@ +package sh.okx.civmodern.common.events; + +public interface Event { + +} diff --git a/common/src/main/java/sh/okx/civmodern/common/events/EventBus.java b/common/src/main/java/sh/okx/civmodern/common/events/EventBus.java new file mode 100644 index 0000000..9d5f280 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/events/EventBus.java @@ -0,0 +1,7 @@ +package sh.okx.civmodern.common.events; + +import java.util.function.Consumer; + +public interface EventBus { + void listen(Class event, Consumer listener); +} diff --git a/common/src/main/java/sh/okx/civmodern/common/events/PostRenderGameOverlayEvent.java b/common/src/main/java/sh/okx/civmodern/common/events/PostRenderGameOverlayEvent.java new file mode 100644 index 0000000..be3baa4 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/events/PostRenderGameOverlayEvent.java @@ -0,0 +1,21 @@ +package sh.okx.civmodern.common.events; + +import com.mojang.blaze3d.vertex.PoseStack; + +public class PostRenderGameOverlayEvent implements Event { + private final PoseStack poseStack; + private final float delta; + + public PostRenderGameOverlayEvent(PoseStack poseStack, float delta) { + this.poseStack = poseStack; + this.delta = delta; + } + + public PoseStack getPoseStack() { + return poseStack; + } + + public float getDelta() { + return delta; + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/Alignment.java b/common/src/main/java/sh/okx/civmodern/common/gui/Alignment.java new file mode 100644 index 0000000..a5ed9e8 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/Alignment.java @@ -0,0 +1,32 @@ +package sh.okx.civmodern.common.gui; + +public enum Alignment { + TOP_LEFT("Top Left"), + TOP_RIGHT("Top Right"), + BOTTOM_RIGHT("Bottom Right"), + BOTTOM_LEFT("Bottom Left"); + + private final String name; + + Alignment(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public Alignment next() { + switch (this) { + case TOP_LEFT: + return TOP_RIGHT; + case TOP_RIGHT: + return BOTTOM_RIGHT; + case BOTTOM_RIGHT: + return BOTTOM_LEFT; + default: + return TOP_LEFT; + } + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/ColorValue.java b/common/src/main/java/sh/okx/civmodern/common/gui/ColorValue.java new file mode 100644 index 0000000..9507aba --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/ColorValue.java @@ -0,0 +1,39 @@ +package sh.okx.civmodern.common.gui; + +import java.text.DecimalFormat; +import java.util.function.Consumer; +import java.util.function.Supplier; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +public class ColorValue implements DoubleValue { + private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("##%"); + + private final String name; + private final int bits; + private final Supplier supplier; + private final Consumer consumer; + + public ColorValue(String name, int bits, Supplier supplier, Consumer consumer) { + this.name = name; + this.bits = bits; + this.supplier = supplier; + this.consumer = consumer; + } + + @Override + public double get() { + return ((supplier.get() >> bits) & 0xFF) / 255d; + } + + @Override + public void set(double value) { + int set = (1 << bits + 8) - (1 << bits); + consumer.accept((supplier.get() & ~set) | ((int) (value * 255d) << bits)); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent(name, PERCENT_FORMAT.format(value)); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/DoubleValue.java b/common/src/main/java/sh/okx/civmodern/common/gui/DoubleValue.java new file mode 100644 index 0000000..ccf7393 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/DoubleValue.java @@ -0,0 +1,9 @@ +package sh.okx.civmodern.common.gui; + +import net.minecraft.network.chat.Component; + +public interface DoubleValue { + double get(); + void set(double value); + Component getText(double value); +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/Texture.java b/common/src/main/java/sh/okx/civmodern/common/gui/Texture.java new file mode 100644 index 0000000..8e85aff --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/Texture.java @@ -0,0 +1,77 @@ +package sh.okx.civmodern.common.gui; + +import static org.lwjgl.opengl.GL11.*; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.TextureUtil; +import java.util.Objects; +import net.minecraft.client.Minecraft; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +/** + * OpenGL interface for a texture binding + */ +public class Texture { + private final int id; + private int width; + private int height; + private int[] pixels; + + public Texture(int width, int height) { + this.id = TextureUtil.generateTextureId(); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + bind(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + resize(width, height); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8, this.pixels); + } + + public void resize(int width, int height) { + this.width = width; + this.height = height; + this.pixels = new int[width * height]; + } + + public void setPixels(int[] pixels) { + if (Objects.requireNonNull(pixels).length != width * height) { + throw new IllegalArgumentException("Pixels length array incorrect, should be " + (width * height) + ", is " + pixels.length); + } + this.pixels = pixels; + } + + public void update() { + bind(); + + // If this is removed you will be fuckedd do not removed this + GL11.glPixelStorei(0xcf0, 0); + GL11.glPixelStorei(0xcf1, 0); + GL11.glPixelStorei(0xcf2, 0); + GL11.glPixelStorei(0xcf3, 0); + GL11.glPixelStorei(0xcf4, 0); + GL11.glPixelStorei(0xcf5, 4); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8, this.pixels); + } + + public void bind() { + GlStateManager._bindTexture(id); + } + + public void unbind() { + GlStateManager._bindTexture(0); + } + + public void delete() { + GlStateManager._deleteTexture(id); + } + + public int getWidth() { + return width; + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/screen/CompactedConfigScreen.java b/common/src/main/java/sh/okx/civmodern/common/gui/screen/CompactedConfigScreen.java new file mode 100644 index 0000000..427b870 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/screen/CompactedConfigScreen.java @@ -0,0 +1,137 @@ +package sh.okx.civmodern.common.gui.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import java.text.DecimalFormat; +import java.util.regex.Pattern; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.gui.widget.HsbColourPicker; +import sh.okx.civmodern.common.gui.widget.ImageButton; + +public class CompactedConfigScreen extends Screen { + private static final DecimalFormat FORMAT = new DecimalFormat("##%"); + + private static final ItemStack ITEM; + + private int itemX; + private int itemY; + + static { + CompoundTag item = new CompoundTag(); + item.putString("id", "stone"); + item.putInt("Count", 64); + CompoundTag tag = new CompoundTag(); + ListTag lore = new ListTag(); + lore.add(StringTag.valueOf("\"Compacted Item\"")); + CompoundTag display = new CompoundTag(); + display.put("Lore", lore); + tag.put("display", display); + item.put("tag", tag); + ITEM = ItemStack.of(item); + } + + private final AbstractCivModernMod mod; + private final CivMapConfig config; + private final Screen parent; + private CommonFont cFont; + + public CompactedConfigScreen(AbstractCivModernMod mod, CivMapConfig config, Screen parent) { + super(new TranslatableComponent("civmodern.screen.compacted.title")); + this.mod = mod; + this.config = config; + this.parent = parent; + } + + @Override + protected void init() { + this.cFont = mod.getCompat().provideFont(this.font); + + itemX = this.width / 2 - 16 / 2; + itemY = this.height / 6 - 24; + + int leftWidth = width / 2 - (60 + 8 + 20 + 8 + 20) / 2; + + EditBox widget = new EditBox(font, leftWidth, height / 6, 60, 20, TextComponent.EMPTY); + widget.setValue("#" + String.format("%06X", config.getColour())); + widget.setMaxLength(7); + Pattern pattern = Pattern.compile("^(#[0-9A-F]{0,6})?$", Pattern.CASE_INSENSITIVE); + widget.setFilter(string -> pattern.matcher(string).matches()); + widget.setResponder(val -> { + if (val.length() == 7) { + int rgb = Integer.parseInt(val.substring(1), 16); + config.setColour(rgb); + } + }); + addButton(widget); + + HsbColourPicker hsb = new HsbColourPicker(leftWidth + 60 + 8, height / 6, 20, 20, config.getColour(), + colour -> { + widget.setValue("#" + String.format("%06X", colour)); + config.setColour(colour); + }); + + addButton(new ImageButton(leftWidth + 60 + 8 + 20 + 8, height / 6, 20, 20, new ResourceLocation("civmodern", "gui/rollback.png"), imbg -> { + int colour = 0xffff58; + widget.setValue("#FFFF58"); + config.setColour(colour); + hsb.close(); + })); + + addButton(hsb); + + + addButton(new Button(this.width / 2 - 49, this.height / 6 + 169, 98, 20, CommonComponents.GUI_DONE, button -> { + Minecraft.getInstance().setScreen(parent); + })); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + super.renderBackground(matrices); + + drawCentredText(matrices, this.title, 0, 40, 0xffffff); + + drawItem(); + + if (isCursorOverItem(mouseX, mouseY)) { + this.renderTooltip(matrices, ITEM, mouseX, mouseY); + } + + super.render(matrices, mouseX, mouseY, delta); + } + + @Override + public void onClose() { + super.onClose(); + config.save(); + } + + private boolean isCursorOverItem(int mouseX, int mouseY) { + return mouseX >= itemX - 1 && mouseX < itemX + 17 && mouseY > itemY - 1 && mouseY < itemY + 17; + } + + private void drawItem() { + itemRenderer.renderGuiItem(ITEM, itemX, itemY); + itemRenderer.renderGuiItemDecorations(font, ITEM, itemX, itemY); + } + + private void drawCentredText(PoseStack matrix, Component text, int xOffsetCentre, int yOffsetTop, int colour) { + int width = Minecraft.getInstance().getWindow().getGuiScaledWidth(); + int centre = width / 2 - font.width(text) / 2; + this.cFont.drawShadow(matrix, text, centre + xOffsetCentre, yOffsetTop, colour); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/screen/IceRoadConfigScreen.java b/common/src/main/java/sh/okx/civmodern/common/gui/screen/IceRoadConfigScreen.java new file mode 100644 index 0000000..657f4f1 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/screen/IceRoadConfigScreen.java @@ -0,0 +1,53 @@ +package sh.okx.civmodern.common.gui.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.TranslatableComponent; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.gui.widget.CyclicButton; + +public class IceRoadConfigScreen extends Screen { + private final AbstractCivModernMod mod; + private final CivMapConfig config; + private final Screen parent; + private CommonFont cFont; + + protected IceRoadConfigScreen(AbstractCivModernMod mod, CivMapConfig config, Screen parent) { + super(new TranslatableComponent("civmodern.screen.ice.title")); + this.mod = mod; + this.config = config; + this.parent = parent; + } + + @Override + protected void init() { + this.cFont = mod.getCompat().provideFont(this.font); + + addButton(new CyclicButton(this.width / 2 - 75, this.height / 6 + 24, 150, 20, config.isIceRoadCardinalEnabled() ? 0 : 1, cycl -> { + config.setIceRoadCardinalEnabled(cycl.getIndex() == 0); + }, new TranslatableComponent("civmodern.screen.ice.cardinal.enable"), new TranslatableComponent("civmodern.screen.ice.cardinal.disable"))); + + addButton(new Button(this.width / 2 - 49, this.height / 6 + 169, 98, 20, CommonComponents.GUI_DONE, button -> { + Minecraft.getInstance().setScreen(parent); + })); + } + + @Override + public void render(PoseStack poseStack, int i, int j, float f) { + super.renderBackground(poseStack); + super.render(poseStack, i, j, f); + + cFont.drawShadowCentred(poseStack, this.title, this.width / 2f, 40, 0xffffff); + } + + @Override + public void onClose() { + super.onClose(); + config.save(); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/screen/MainConfigScreen.java b/common/src/main/java/sh/okx/civmodern/common/gui/screen/MainConfigScreen.java new file mode 100644 index 0000000..596a69a --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/screen/MainConfigScreen.java @@ -0,0 +1,47 @@ +package sh.okx.civmodern.common.gui.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.TranslatableComponent; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.compat.CommonFont; + +public class MainConfigScreen extends Screen { + + private final AbstractCivModernMod mod; + private final CivMapConfig config; + private CommonFont cFont; + + public MainConfigScreen(AbstractCivModernMod mod, CivMapConfig config) { + super(new TranslatableComponent("civmodern.screen.main.title")); + this.mod = mod; + this.config = config; + } + + @Override + protected void init() { + this.cFont = mod.getCompat().provideFont(this.font); + + int col0 = this.width / 2 - 150 / 2; + addButton(new Button(col0, this.height / 6, 150, 20, new TranslatableComponent("civmodern.screen.main.compacted"), button -> { + Minecraft.getInstance().setScreen(new CompactedConfigScreen(mod, config, this)); + })); + addButton(new Button(col0, this.height / 6 + 24, 150, 20, new TranslatableComponent("civmodern.screen.main.radar"), button -> { + Minecraft.getInstance().setScreen(new RadarConfigScreen(mod, config, this)); + })); + addButton(new Button(col0, this.height / 6 + 48, 150, 20, new TranslatableComponent("civmodern.screen.main.ice"), button -> { + Minecraft.getInstance().setScreen(new IceRoadConfigScreen(mod, config, this)); + })); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + super.renderBackground(matrices); + super.render(matrices, mouseX, mouseY, delta); + + cFont.drawShadow(matrices, this.title, this.width / 2f - this.font.width(this.title) / 2f, 40, 0xffffff); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/screen/RadarConfigScreen.java b/common/src/main/java/sh/okx/civmodern/common/gui/screen/RadarConfigScreen.java new file mode 100644 index 0000000..ee27218 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/screen/RadarConfigScreen.java @@ -0,0 +1,266 @@ +package sh.okx.civmodern.common.gui.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import java.text.DecimalFormat; +import java.util.regex.Pattern; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.gui.widget.DoubleOptionUpdateableSliderWidget; +import sh.okx.civmodern.common.gui.DoubleValue; +import sh.okx.civmodern.common.gui.widget.HsbColourPicker; +import sh.okx.civmodern.common.gui.widget.ImageButton; + +public class RadarConfigScreen extends Screen { + + private final AbstractCivModernMod mod; + private final CivMapConfig config; + private final Screen parent; + private CommonFont cFont; + + public RadarConfigScreen(AbstractCivModernMod mod, CivMapConfig config, Screen parent) { + super(new TranslatableComponent("civmodern.screen.radar.title")); + this.mod = mod; + this.config = config; + this.parent = parent; + } + + @Override + protected void init() { + this.cFont = mod.getCompat().provideFont(this.font); + + int centre = this.width / 2 - 98 / 2; + addButton(new Button(this.width / 2 - 75, this.height / 6 + 24, 150, 20, getRadarToggleMessage(), button -> { + config.setRadarEnabled(!config.isRadarEnabled()); + button.setMessage(getRadarToggleMessage()); + })); + addButton(new Button(this.width / 2 - 75, this.height / 6 + 48, 150, 20, getPingToggleMessage(), button -> { + config.setPingEnabled(!config.isPingEnabled()); + button.setMessage(getPingToggleMessage()); + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 24, 150, 20, 1, 8, 1, new DoubleValue() { + @Override + public double get() { + return config.getRadarCircles(); + } + + @Override + public void set(double value) { + config.setRadarCircles((int) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.circles", + Integer.toString((int) value)); + } + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 48, 150, 20, 50, 300, 1, new DoubleValue() { + @Override + public double get() { + return config.getRadarSize(); + } + + @Override + public void set(double value) { + config.setRadarSize((int) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.size", + Integer.toString((int) value)); + } + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 72, 150, 20, 0.5, 2, 0.1, new DoubleValue() { + private final DecimalFormat format = new DecimalFormat("#.#"); + + @Override + public double get() { + return config.getIconSize(); + } + + @Override + public void set(double value) { + config.setIconSize((float) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.iconsize", format.format(value)); + } + })); + addButton(new Button(this.width / 2 - 75, this.height / 6 + 48 + 96, 150, 20, new TranslatableComponent("civmodern.screen.radar.alignment", config.getAlignment().toString()), button -> { + config.setAlignment(config.getAlignment().next()); + button.setMessage(new TranslatableComponent("civmodern.screen.radar.alignment", config.getAlignment().toString())); + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 120, 150, 20, 20, 150, 1, new DoubleValue() { + + @Override + public double get() { + return config.getRange(); + } + + @Override + public void set(double value) { + config.setRange((int) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.range", String.valueOf((int) value)); + } + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 144, 150, 20, 0, 300, 1, new DoubleValue() { + + @Override + public double get() { + return config.getX(); + } + + @Override + public void set(double value) { + config.setX((int) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.x", String.valueOf((int) value)); + } + })); + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 48 + 168, 150, 20, 0, 300, 1, new DoubleValue() { + + @Override + public double get() { + return config.getY(); + } + + @Override + public void set(double value) { + config.setY((int) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.y", String.valueOf((int) value)); + } + })); + + addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 168 + 48 + 24, 150, 20, 0, 1, 0.01, new DoubleValue() { + private final DecimalFormat format = new DecimalFormat("##%"); + + @Override + public double get() { + return config.getTransparency(); + } + + @Override + public void set(double value) { + config.setTransparency((float) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.transparency", format.format(value)); + } + })); + + int left = width / 2 - (60 + 8 + 20 + 8 + 20) / 2; + EditBox widget = new EditBox(font, left, height / 6 + 48 + 48 + 168, 60, 20, TextComponent.EMPTY); + widget.setValue("#" + String.format("%06X", config.getRadarColour())); + widget.setMaxLength(7); + Pattern pattern = Pattern.compile("^(#[0-9A-F]{0,6})?$", Pattern.CASE_INSENSITIVE); + widget.setFilter(string -> pattern.matcher(string).matches()); + widget.setResponder(val -> { + if (val.length() == 7) { + int rgb = Integer.parseInt(val.substring(1), 16); + config.setRadarColour(rgb); + } + }); + addButton(widget); + + HsbColourPicker hsb = new HsbColourPicker(left + 60 + 8, height / 6 + 48 + 48 + 168, + 20, 20, config.getRadarColour(), colour -> { + widget.setValue("#" + String.format("%06X", colour)); + config.setRadarColour(colour); + }); + addButton(new ImageButton(left + 60 + 8 + 20 + 8, height / 6 + 48 + 48 + 168, 20, 20, new ResourceLocation("civmodern", "gui/rollback.png"), imbg -> { + int colour = 0x888888; + widget.setValue("#888888"); + config.setRadarColour(colour); + hsb.close(); + })); + addButton(hsb); + + /*addButton(new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 24 + 150 + 48, 150, 20, 0, 1, 0.01, new DoubleValue() { + private final DecimalFormat format = new DecimalFormat("##%"); + + @Override + public double get() { + return config.getTransparency(); + } + + @Override + public void set(double value) { + config.setTransparency((float) value); + } + + @Override + public Component getText(double value) { + return new TranslatableComponent("civmodern.screen.radar.transparency", format.format(value)); + } + })); + DoubleOptionUpdateableSliderWidget redOption = new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 24 + 172 + 4 + 48, 150, 20, 0, 1, 1f / 255, + new ColorValue("civmodern.configscreen.colour.red", 16, config::getRadarColour, config::setRadarColour)); + addButton(redOption); + DoubleOptionUpdateableSliderWidget greenOption = new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 24 + 196 + 4 + 48, 150, 20, 0, 1, 1f / 255, + new ColorValue("civmodern.configscreen.colour.green", 8, config::getRadarColour, config::setRadarColour)); + addButton(greenOption); + DoubleOptionUpdateableSliderWidget blueOption = new DoubleOptionUpdateableSliderWidget(this.width / 2 - 75, this.height / 6 + 24 + 220 + 4 + 48, 150, 20, 0, 1, 1f / 255, + new ColorValue("civmodern.configscreen.colour.blue", 0, config::getRadarColour, config::setRadarColour)); + addButton(blueOption);*/ + + addButton(new Button(centre, this.height / 6 + 24 + 265 + 48, 98, 20, CommonComponents.GUI_DONE, button -> { + Minecraft.getInstance().setScreen(parent); + })); + } + + private Component getPingToggleMessage() { + if (config.isPingEnabled()) { + return new TextComponent("Pings: Enabled"); + } else { + return new TextComponent("Pings: Disabled"); + } + } + + @Override + public void onClose() { + config.save(); + super.onClose(); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + //super.renderBackground(matrices); + super.render(matrices, mouseX, mouseY, delta); + + this.cFont.drawShadowCentred(matrices, this.title, this.width / 2f, 40, 0xffffff); + } + + private Component getRadarToggleMessage() { + if (config.isRadarEnabled()) { + return new TextComponent("Radar: Enabled"); + } else { + return new TextComponent("Radar: Disabled"); + } + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/widget/CyclicButton.java b/common/src/main/java/sh/okx/civmodern/common/gui/widget/CyclicButton.java new file mode 100644 index 0000000..da97472 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/widget/CyclicButton.java @@ -0,0 +1,33 @@ +package sh.okx.civmodern.common.gui.widget; + +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; + +public class CyclicButton extends Button { + + private final OnPress onPress; + private final Component[] components; + private int index; + + public CyclicButton(int x, int y, int width, int height, int index, OnPress onPress, Component... components) { + super(x, y, width, height, components[index], b -> {}); + this.index = index; + this.components = components; + this.onPress = onPress; + } + + public int getIndex() { + return index; + } + + @Override + public void onPress() { + index = (index + 1) % components.length; + this.setMessage(components[index]); + onPress.onPress(this); + } + + public interface OnPress { + void onPress(CyclicButton button); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/widget/DoubleOptionUpdateableSliderWidget.java b/common/src/main/java/sh/okx/civmodern/common/gui/widget/DoubleOptionUpdateableSliderWidget.java new file mode 100644 index 0000000..2f5d2d1 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/widget/DoubleOptionUpdateableSliderWidget.java @@ -0,0 +1,38 @@ +package sh.okx.civmodern.common.gui.widget; + +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.util.Mth; +import sh.okx.civmodern.common.gui.DoubleValue; + +public class DoubleOptionUpdateableSliderWidget extends AbstractSliderButton { + private final DoubleValue param; + private final double min; + private final double max; + private final double step; + + public DoubleOptionUpdateableSliderWidget(int x, int y, + int width, int height, double min, double max, double step, DoubleValue param) { + super(x, y, width, height, TextComponent.EMPTY, (param.get() - min) / (max - min)); + this.param = param; + this.min = min; + this.max = max; + this.step = step; + this.updateMessage(); + } + + public void update() { + updateMessage(); + this.value = (param.get() - min) / (max - min); + } + + @Override + protected void updateMessage() { + this.setMessage(this.param.getText(this.param.get())); + } + + @Override + protected void applyValue() { + param.set(Mth.lerp(this.value, min, max)); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/widget/HsbColourPicker.java b/common/src/main/java/sh/okx/civmodern/common/gui/widget/HsbColourPicker.java new file mode 100644 index 0000000..ab501f4 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/widget/HsbColourPicker.java @@ -0,0 +1,190 @@ +package sh.okx.civmodern.common.gui.widget; + +import static org.lwjgl.opengl.GL11.*; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import java.awt.Color; +import java.util.function.Consumer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import sh.okx.civmodern.common.gui.Texture; + +public class HsbColourPicker extends AbstractWidget { + + private final Texture hueSelector; + private final Texture saturationBrightnessTexture; + private final Consumer colourConsumer; + + private int hue = 0; // [0, 360] + private int saturation = 0; // [0, 100] + private int brightness = 0; // [0, 100] + + private boolean showPalette = false; + private boolean updateTexture = true; + private boolean hueMouseDown = false; + + + public HsbColourPicker(int x, int y, int width, int height, int colour, Consumer colourConsumer) { + super(x, y, width, height, new TextComponent("HSB Colour Picker")); + + this.hue = Math.round(Color.RGBtoHSB(colour >> 16 & 0xFF, colour >> 8 & 0xFF, colour & 0xFF, null)[0] * 360); + + this.hueSelector = HsbColourPicker.getHueSelector(); + this.saturationBrightnessTexture = new Texture(128, 128); + this.colourConsumer = colourConsumer; + } + + @Override + public void renderButton(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { + Minecraft minecraft = Minecraft.getInstance(); + minecraft.getTextureManager().bind(new ResourceLocation("civmodern", "gui/colour.png")); + RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha); + Gui.blit(matrixStack, this.x, this.y, this.getBlitOffset(), 0, isHovered() ? 20 : 0, this.width, + this.height, 40, 20); + + if (showPalette) { + RenderSystem.enableTexture(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + if (this.updateTexture) { + updateTexture(this.saturationBrightnessTexture, this.hue); + this.updateTexture = false; + } else { + this.saturationBrightnessTexture.bind(); + } + + glPushMatrix(); + glTranslatef(0, 0, 1000); + // Saturation and brightness selector + Gui.blit(matrixStack, this.x, this.y + height, 0, 0, 0, 101, 101, 128, 128); + + // Hue selector + hueSelector.bind(); + Gui.blit(matrixStack, this.x + 106, this.y + height, 10, 101, 0, 0, 1, 360, 1, 360); + + RenderSystem.disableTexture(); + // Render cursor + glEnable(GL_LINE_SMOOTH); + glLineWidth(2f); + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder buffer = tessellator.getBuilder(); + buffer.begin(GL_LINES, DefaultVertexFormat.POSITION); + + double hueOffset = (this.hue / 360f) * 101; + buffer.vertex(this.x + 106, this.y + height + hueOffset, 0f).endVertex(); + buffer.vertex(this.x + 116, this.y + height + hueOffset, 0f).endVertex(); + + buffer.end(); + BufferUploader.end(buffer); + glDisable(GL_LINE_SMOOTH); + + glPopMatrix(); + + RenderSystem.enableTexture(); + RenderSystem.disableBlend(); + RenderSystem.disableAlphaTest(); + + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + showPalette = !showPalette; + } + + @Override + protected void onDrag(double mouseX, double mouseY, double dragX, double dragY) { + if (this.hueMouseDown) { + setHue(mouseX, mouseY, 0, true); + } + } + + @Override + public void onRelease(double d, double e) { + this.hueMouseDown = false; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return selectColour(mouseX, mouseY, button) + || setHue(mouseX, mouseY, button, false) + || super.mouseClicked(mouseX, mouseY, button); + } + + private boolean selectColour(double mouseX, double mouseY, int button) { + if (active && visible && button == 0 && showPalette) { + if (mouseX >= this.x && mouseX < this.x + 101 + && mouseY >= this.y + height && mouseY < this.y + height + 101) { + this.saturation = (int) (mouseX - this.x); + this.brightness = (int) (mouseY - this.y - height); + colourConsumer.accept(toRgb()); + this.showPalette = false; + return true; + } + } + return false; + } + + private boolean setHue(double mouseX, double mouseY, int button, boolean force) { + // Cursor selector + if (active && visible && button == 0 && showPalette) { + if (!(mouseY >= this.y + height && mouseY <= this.y + height + 101)) { + return false; + } + + if (force || (mouseX >= this.x + 106 && mouseX <= this.x + 106 + 10)) { + this.hueMouseDown = true; + double yOffset = mouseY - (this.y + height); + int newHue = (int) ((yOffset / 101) * 360); + if (newHue != this.hue) { + this.hue = newHue; + this.updateTexture = true; + } + return true; + } + } + return false; + } + + private void updateTexture(Texture texture, int hue) { + int[] rgbaValues = new int[128 * 128]; + for (int saturation = 0; saturation <= 100; saturation++) { + for (int brightness = 0; brightness <= 100; brightness++) { + int rgb = Color.HSBtoRGB(hue / 360f, saturation / 100f, brightness / 100f) & 0xFFFFFF; + rgbaValues[(brightness * 128) + saturation] = rgb << 8 | 0xFF; + } + } + texture.setPixels(rgbaValues); + texture.update(); + } + + private int toRgb() { + return Color.HSBtoRGB(this.hue / 360f, this.saturation / 100f, this.brightness / 100f) & 0xFFFFFF; + } + + public static Texture getHueSelector() { + Texture hueSelector = new Texture(1, 360); + int[] rgbaValues = new int[360]; + for (int i = 0; i < 360; i++) { + int rgb = Color.HSBtoRGB(i / 360f, 1, 1); + rgbaValues[i] = rgb << 8 | 0xFF; + } + hueSelector.setPixels(rgbaValues); + hueSelector.update(); + return hueSelector; + } + + public void close() { + this.showPalette = false; + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/gui/widget/ImageButton.java b/common/src/main/java/sh/okx/civmodern/common/gui/widget/ImageButton.java new file mode 100644 index 0000000..f0ab79c --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/gui/widget/ImageButton.java @@ -0,0 +1,43 @@ +package sh.okx.civmodern.common.gui.widget; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; + +public class ImageButton extends AbstractWidget { + + private final ResourceLocation image; + private final OnPress onPress; + + public ImageButton(int x, int y, int width, int height, ResourceLocation image, OnPress onPress) { + super(x, y, width, height, TextComponent.EMPTY); + this.image = image; + this.onPress = onPress; + } + + @Override + public void renderButton(PoseStack poseStack, int i, int j, float f) { + Minecraft minecraft = Minecraft.getInstance(); + minecraft.getTextureManager().bind(image); + RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha); + int k = this.isHovered() ? 1 : 0; + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableDepthTest(); + Gui.blit(poseStack, this.x, this.y, this.getBlitOffset(), 0, k * 20, this.width, this.height, 40, 20); + this.renderBg(poseStack, minecraft, i, j); + } + + @Override + public void onClick(double d, double e) { + this.onPress.onPress(this); + } + + public interface OnPress { + void onPress(ImageButton button); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/macro/HoldKeyMacro.java b/common/src/main/java/sh/okx/civmodern/common/macro/HoldKeyMacro.java new file mode 100644 index 0000000..43190c9 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/macro/HoldKeyMacro.java @@ -0,0 +1,55 @@ +package sh.okx.civmodern.common.macro; + +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.events.ClientTickEvent; + +public class HoldKeyMacro { + + private final KeyMapping holdBinding; + private final KeyMapping defaultBinding; + private boolean down = false; + + public HoldKeyMacro(AbstractCivModernMod mod, KeyMapping holdBinding, KeyMapping defaultBinding) { + mod.getEventBus().listen(ClientTickEvent.class, e -> tick()); + this.holdBinding = holdBinding; + this.defaultBinding = defaultBinding; + } + + public void tick() { + Minecraft mc = Minecraft.getInstance(); + if (mc.player == null) { + return; + } + + if (down && (mc.screen != null || !mc.mouseHandler.isMouseGrabbed())) { + set(false); + return; + } + + for (KeyMapping hotbar : mc.options.keyHotbarSlots) { + if (hotbar.isDown()) { + set(false); + return; + } + } + + while (holdBinding.consumeClick()) { + if (this.down) { + set(false); + } else if (!mc.player.isUsingItem()) { + set(true); + } + } + } + + public void onScroll() { + set(false); + } + + private void set(boolean down) { + this.down = down; + this.defaultBinding.setDown(down); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/macro/IceRoadMacro.java b/common/src/main/java/sh/okx/civmodern/common/macro/IceRoadMacro.java new file mode 100644 index 0000000..e1092a7 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/macro/IceRoadMacro.java @@ -0,0 +1,60 @@ +package sh.okx.civmodern.common.macro; + +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.events.ClientTickEvent; + +public class IceRoadMacro { + private final CivMapConfig config; + private final KeyMapping key; + // alternate between true and false for maximum jumpage + private boolean enabled = false; + private boolean jump = false; + + public IceRoadMacro(AbstractCivModernMod mod, CivMapConfig config, KeyMapping key) { + // Options for ice road macro: + // Auto cardinal + // Auto eat + + mod.getEventBus().listen(ClientTickEvent.class, e -> tick()); + this.config = config; + this.key = key; + } + + public void tick() { + Minecraft mc = Minecraft.getInstance(); + + while (this.key.consumeClick()) { + if (enabled) { + mc.options.keySprint.setDown(false); + mc.options.keyUp.setDown(false); + if (jump) { + jump = false; + mc.options.keyJump.setDown(false); + } + enabled = false; + } else { + if (config.isIceRoadCardinalEnabled()) { + float rot = Math.round(mc.player.yRot / 45); + mc.player.yRot = rot * 45; + } + + enabled = true; + } + } + + if (enabled) { + if (!jump) { + mc.options.keyJump.setDown(true); + jump = true; + } else { + mc.options.keyJump.setDown(false); + jump = false; + } + mc.options.keySprint.setDown(true); + mc.options.keyUp.setDown(true); + } + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/mixins/InventoryMixin.java b/common/src/main/java/sh/okx/civmodern/common/mixins/InventoryMixin.java new file mode 100644 index 0000000..b37fc1c --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/mixins/InventoryMixin.java @@ -0,0 +1,16 @@ +package sh.okx.civmodern.common.mixins; + +import net.minecraft.world.entity.player.Inventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import sh.okx.civmodern.common.AbstractCivModernMod; + +@Mixin(Inventory.class) +public class InventoryMixin { + @Inject(at = @At("HEAD"), method = "swapPaint(D)V") + private void swapPaint(CallbackInfo info) { + AbstractCivModernMod.staticOnScroll(); + } +} diff --git a/common/src/main/java/sh/okx/civmodern/common/radar/Radar.java b/common/src/main/java/sh/okx/civmodern/common/radar/Radar.java new file mode 100644 index 0000000..7054e32 --- /dev/null +++ b/common/src/main/java/sh/okx/civmodern/common/radar/Radar.java @@ -0,0 +1,454 @@ +package sh.okx.civmodern.common.radar; + +import static org.lwjgl.opengl.GL11.*; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.screens.ChatScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.player.RemotePlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.KeybindComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.Minecart; +import sh.okx.civmodern.common.CivMapConfig; +import sh.okx.civmodern.common.compat.CommonFont; +import sh.okx.civmodern.common.compat.CompatProvider; +import sh.okx.civmodern.common.events.ClientTickEvent; +import sh.okx.civmodern.common.events.EventBus; +import sh.okx.civmodern.common.events.PostRenderGameOverlayEvent; +import sh.okx.civmodern.common.gui.screen.RadarConfigScreen; + +public class Radar { + + private static final double COS_45 = 0.70710677D; + private static final int PLAYER_RANGE = 70; + + private final EventBus eventBus; + private final CivMapConfig config; + private final CommonFont cFont; + + private Set playersInRange = new HashSet<>(); + private String lastWaypointCommand; + + + public Radar(CivMapConfig config, EventBus eventBus, CompatProvider compatProvider) { + this.config = config; + this.eventBus = eventBus; + this.cFont = compatProvider.provideFont(Minecraft.getInstance().font); + } + + public void init() { + eventBus.listen(ClientTickEvent.class, this::onClientTick); + eventBus.listen(ClientTickEvent.class, this::onWorldTickPing); + eventBus.listen(PostRenderGameOverlayEvent.class, this::onRender); + } + + public void onClientTick(ClientTickEvent event) { + Minecraft client = Minecraft.getInstance(); + if (client.level == null) { + this.playersInRange = Collections.emptySet(); + } + } + + public void onWorldTickPing(ClientTickEvent event) { + ClientLevel world = Minecraft.getInstance().level; + if (world == null || !config.isPingEnabled()) { + return; + } + + Set newPlayersInRange = new HashSet<>(); + for (Entity entity : world.entitiesForRendering()) { + if (entity instanceof RemotePlayer) { + RemotePlayer player = (RemotePlayer) entity; + newPlayersInRange.add(player); + if (!playersInRange.contains(player)) { + BlockPos pos = player.blockPosition(); + lastWaypointCommand = + "/newWaypoint x:" + pos.getX() + ",y:" + pos.getY() + ",z:" + pos.getZ() + ",name:" + + player.getScoreboardName(); + Minecraft.getInstance().player.displayClientMessage( + new TranslatableComponent("civmodern.radar.enter", + player.getName(), + new TextComponent(Integer.toString(pos.getX())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA)), + new TextComponent(Integer.toString(pos.getY())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA)), + new TextComponent(Integer.toString(pos.getZ())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA))) + .setStyle(Style.EMPTY + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, lastWaypointCommand)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new TranslatableComponent("civmodern.radar.hover", + new KeybindComponent("key.civmodern.highlight"))))), + false); + + } + } + } + + for (RemotePlayer player : playersInRange) { + if (!newPlayersInRange.contains(player)) { + BlockPos pos = player.blockPosition(); + lastWaypointCommand = + "/newWaypoint x:" + pos.getX() + ",y:" + pos.getY() + ",z:" + pos.getZ() + ",name:" + + player.getScoreboardName(); + Minecraft.getInstance().player.displayClientMessage( + new TranslatableComponent("civmodern.radar.leave", + player.getName(), + new TextComponent(Integer.toString(pos.getX())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA)), + new TextComponent(Integer.toString(pos.getY())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA)), + new TextComponent(Integer.toString(pos.getZ())) + .withStyle(s -> s.applyFormat(ChatFormatting.AQUA))) + .setStyle(Style.EMPTY + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, lastWaypointCommand)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new TranslatableComponent("civmodern.radar.hover", + new KeybindComponent("key.civmodern.highlight"))))), + false); + } + } + + this.playersInRange = newPlayersInRange; + } + + public void onRender(PostRenderGameOverlayEvent event) { + Screen screen = Minecraft.getInstance().screen; + if (screen == null + || screen instanceof RadarConfigScreen + || screen instanceof ChatScreen + //|| screen instanceof IngameMenuScreen + ) { + if (config.isRadarEnabled()) { + render(event.getPoseStack(), event.getDelta()); + } + } + } + + private int radius() { + return config.getRadarSize(); + } + + public void render(PoseStack matrices, float delta) { + Minecraft client = Minecraft.getInstance(); + LocalPlayer player = client.player; + glPushMatrix(); + + int offsetX = config.getX(); + int offsetY = config.getY(); + int z = 1000; + + int height = client.getWindow().getGuiScaledHeight(); + int width = client.getWindow().getGuiScaledWidth(); + switch (config.getAlignment()) { + case TOP_LEFT: + glTranslatef(offsetX, offsetY, z); + break; + case TOP_RIGHT: + glTranslatef(width - offsetX, offsetY, z); + break; + case BOTTOM_RIGHT: + glTranslatef(width - offsetX, height - offsetY, z); + break; + case BOTTOM_LEFT: + glTranslatef(offsetX, height - offsetY, z); + break; + } + float yaw = player.getViewYRot(delta); + glRotatef(-yaw, 0, 0, 1); + + renderCircleBackground(); + for (int i = 1; i <= config.getRadarCircles(); i++) { + renderCircleBorder(radius() * (i / (double) config.getRadarCircles())); + } + //renderRange(); + renderLines(); + + renderBoatsMinecarts(matrices, delta); + renderPlayers(matrices, delta); + + glPopMatrix(); + glColor4f(1.0F, 1.0F, 1.0F, 1); + } + + private void renderRange() { + glLineWidth(1f); + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glEnable(GL_LINE_SMOOTH); + glDisable(GL_LIGHTING); + glColor4f(1, 1, 1, 1); + + // If config.range < 98, then cut off a bit + double len = PLAYER_RANGE * (config.getRadarSize() / config.getRange()); + double corner = config.getRadarSize() * COS_45; + len = Math.min(len, corner); + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder buffer = tessellator.getBuilder(); + buffer.begin(GL_LINES, DefaultVertexFormat.POSITION); + + buffer.vertex(-len, -len, 0f).endVertex(); + buffer.vertex(-len, len, 0f).endVertex(); + + buffer.vertex(-len, len, 0f).endVertex(); + buffer.vertex(len, len, 0f).endVertex(); + + buffer.vertex(len, len, 0f).endVertex(); + buffer.vertex(len, -len, 0f).endVertex(); + + buffer.vertex(len, -len, 0f).endVertex(); + buffer.vertex(-len, -len, 0f).endVertex(); + + tessellator.end(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glEnable(GL_TEXTURE_2D); + } + + private void renderBoatsMinecarts(PoseStack matrices, float delta) { + Minecraft minecraft = Minecraft.getInstance(); + + for (Entity entity : minecraft.level.entitiesForRendering()) { + if (entity instanceof Boat) { + Boat boat = (Boat) entity; + + double scale = config.getRadarSize() / config.getRange(); + + double px = minecraft.player.xOld + + (minecraft.player.getX() - minecraft.player.xOld) * delta; + double pz = minecraft.player.zOld + + (minecraft.player.getZ() - minecraft.player.zOld) * delta; + double x = boat.xOld + (boat.getX() - boat.xOld) * delta; + double z = boat.zOld + (boat.getZ() - boat.zOld) * delta; + double dx = px - x; + double dz = pz - z; + if (dx * dx + dz * dz > config.getRange() * config.getRange()) { + continue; + } + glEnable(GL_BLEND); + glColor4f(1.0F, 1.0F, 1.0F, 1); + + glPushMatrix(); + glTranslated(dx * scale, dz * scale, 0); + glRotatef(minecraft.player.getViewYRot(delta), 0, 0, 1); + + glPushMatrix(); + matrices.pushPose(); + matrices.scale(config.getIconSize(), config.getIconSize(), 0); + minecraft.getTextureManager().bind( + new ResourceLocation("textures/item/" + boat.getBoatType().getName() + "_boat.png")); + RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); + Gui.blit(matrices, -4, -4, 8, 8, 0, 0, 16, 16, 16, 16); + matrices.popPose(); + + glPopMatrix(); + glPopMatrix(); + } else if (entity instanceof Minecart) { + Minecart minecart = (Minecart) entity; + + double scale = config.getRadarSize() / config.getRange(); + + double px = + minecraft.player.xOld + (minecraft.player.getX() - minecraft.player.xOld) * delta; + double pz = + minecraft.player.zOld + (minecraft.player.getZ() - minecraft.player.zOld) * delta; + double x = minecart.xOld + (minecart.getX() - minecart.xOld) * delta; + double z = minecart.zOld + (minecart.getZ() - minecart.zOld) * delta; + double dx = px - x; + double dz = pz - z; + if (dx * dx + dz * dz > config.getRange() * config.getRange()) { + continue; + } + glEnable(GL_BLEND); + glColor4f(1.0F, 1.0F, 1.0F, 1); + + glPushMatrix(); + glTranslated(dx * scale, dz * scale, 0); + glRotatef(minecraft.player.getViewYRot(delta), 0, 0, 1); + + glPushMatrix(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + matrices.pushPose(); + matrices.scale(config.getIconSize(), config.getIconSize(), 0); + minecraft.getTextureManager().bind(new ResourceLocation("textures/item/minecart.png")); + RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); + Gui.blit(matrices, -4, -4, 8, 8, 0, 0, 16, 16, 16, 16); + matrices.popPose(); + + glPopMatrix(); + glDisable(GL_BLEND); + glPopMatrix(); + } + } + } + + private void renderPlayers(PoseStack matrices, float delta) { + Minecraft minecraft = Minecraft.getInstance(); + + glPushMatrix(); + + for (RemotePlayer player : playersInRange) { + if (!player.isAlive()) { + continue; + } + double v = config.getRadarSize() / config.getRange(); + + double px = minecraft.player.xOld + (minecraft.player.getX() - minecraft.player.xOld) * delta; + double pz = minecraft.player.zOld + (minecraft.player.getZ() - minecraft.player.zOld) * delta; + double x = player.xOld + (player.getX() - player.xOld) * delta; + double z = player.zOld + (player.getZ() - player.zOld) * delta; + double dx = px - x; + double dz = pz - z; + if (dx * dx + dz * dz > config.getRange() * config.getRange()) { + continue; + } + glEnable(GL_BLEND); + glColor4f(1.0F, 1.0F, 1.0F, 1); + + glPushMatrix(); + glTranslated(dx * v, dz * v, 0); + glRotatef(minecraft.player.getViewYRot(delta), 0, 0, 1); + + PlayerInfo entry = minecraft.player.connection.getPlayerInfo(player.getUUID()); + glPushMatrix(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + matrices.pushPose(); + matrices.scale(config.getIconSize(), config.getIconSize(), 0); + if (entry != null) { + minecraft.getTextureManager().bind(entry.getSkinLocation()); + } else { + minecraft.getTextureManager().bind(new ResourceLocation("textures/entity/steve.png")); + } + Gui.blit(matrices, -4, -4, 8, 8, 8.0F, 8, 8, 8, 64, 64); + matrices.pushPose(); + glDisable(GL_BLEND); + matrices.scale(0.6f, 0.6f, 0); + TextComponent component = new TextComponent( + player.getScoreboardName() + " (" + ((int) player.getY() + ")")); + this.cFont.draw(matrices, component, -minecraft.font.width(component) / 2f, 7, 0xffffff); + matrices.popPose(); + matrices.popPose(); + + glPopMatrix(); + glPopMatrix(); + } + + glPopMatrix(); + } + + private void renderCircleBackground() { + glLineWidth(1); + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f((config.getRadarColour() >> 16 & 0xFF) / 255f, + (config.getRadarColour() >> 8 & 0xFF) / 255f, (config.getRadarColour() & 0xFF) / 255f, + 1 - config.getTransparency()); + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder buffer = tessellator.getBuilder(); + buffer.begin(GL_TRIANGLE_FAN, DefaultVertexFormat.POSITION); + + for (int i = 0; i <= 360; i++) { + double x = Math.sin(i * Math.PI / 180.0D) * radius(); + double y = Math.cos(i * Math.PI / 180.0D) * radius(); + buffer.vertex(x, y, 0.0D).endVertex(); + } + tessellator.end(); + + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + + private void renderCircleBorder(double radius) { + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glEnable(GL_POLYGON_SMOOTH); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder buffer = tessellator.getBuilder(); + buffer.begin(GL_TRIANGLE_STRIP, DefaultVertexFormat.POSITION); + + double thicknesss = radius == radius() ? 1 : 0.5; + + for (int i = 0; i <= 360; i++) { + glColor4f((config.getRadarColour() >> 16 & 0xFF) / 255f, + (config.getRadarColour() >> 8 & 0xFF) / 255f, (config.getRadarColour() & 0xFF) / 255f, 1); + double x0 = Math.sin(i * Math.PI / 180.0D) * radius; + double y0 = Math.cos(i * Math.PI / 180.0D) * radius; + + double x1 = Math.sin(i * Math.PI / 180.0D) * (radius + thicknesss); + double y1 = Math.cos(i * Math.PI / 180.0D) * (radius + thicknesss); + buffer.vertex(x0, y0, 0.0D).endVertex(); + buffer.vertex(x1, y1, 0).endVertex(); + } + tessellator.end(); + + glDisable(GL_POLYGON_SMOOTH); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + + private void renderLines() { + glLineWidth(1f); + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glEnable(GL_LINE_SMOOTH); + glDisable(GL_LIGHTING); + glColor4f((config.getRadarColour() >> 16 & 0xFF) / 255f, + (config.getRadarColour() >> 8 & 0xFF) / 255f, (config.getRadarColour() & 0xFF) / 255f, 1); + + int scale = 0; + float radius = radius() + 0.5f; + double diagonalInner = COS_45; + double diagonalOuter = COS_45 * radius; + + Tesselator tessellator = Tesselator.getInstance(); + BufferBuilder buffer = tessellator.getBuilder(); + buffer.begin(GL_LINES, DefaultVertexFormat.POSITION); + + buffer.vertex(0, -radius, 0f).endVertex(); + buffer.vertex(0, radius, 0f).endVertex(); + + buffer.vertex(-radius, 0, 0f).endVertex(); + buffer.vertex(radius, 0, 0f).endVertex(); + + buffer.vertex(-diagonalOuter, -diagonalOuter, 0f).endVertex(); + buffer.vertex(diagonalOuter, diagonalOuter, 0f).endVertex(); + + buffer.vertex(-diagonalOuter, diagonalOuter, 0f).endVertex(); + buffer.vertex(diagonalOuter, -diagonalOuter, 0f).endVertex(); + + tessellator.end(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glEnable(GL_TEXTURE_2D); + } +} diff --git a/common/src/main/resources/assets/civmodern/gui/colour.png b/common/src/main/resources/assets/civmodern/gui/colour.png new file mode 100644 index 0000000..fa1282a Binary files /dev/null and b/common/src/main/resources/assets/civmodern/gui/colour.png differ diff --git a/common/src/main/resources/assets/civmodern/gui/rollback.png b/common/src/main/resources/assets/civmodern/gui/rollback.png new file mode 100644 index 0000000..4b673cf Binary files /dev/null and b/common/src/main/resources/assets/civmodern/gui/rollback.png differ diff --git a/common/src/main/resources/assets/civmodern/lang/en_us.json b/common/src/main/resources/assets/civmodern/lang/en_us.json new file mode 100644 index 0000000..03ea6ab --- /dev/null +++ b/common/src/main/resources/assets/civmodern/lang/en_us.json @@ -0,0 +1,28 @@ +{ + "key.civmodern.left": "Hold left mouse button", + "key.civmodern.right": "Hold right mouse button", + "key.civmodern.ice": "Toggle ice road macro", + "key.civmodern.config": "Open Civ Modern configuration", + "key.civmodern.highlight": "Highlight waypoint", + "category.civmodern": "Civ Modern", + "civmodern.screen.main.title": "Civ Modern Config", + "civmodern.screen.main.radar": "Radar Settings", + "civmodern.screen.main.ice": "Ice Road Macro", + "civmodern.screen.main.compacted": "Compacted Item Settings", + "civmodern.screen.radar.title": "Civ Modern Config (Radar)", + "civmodern.screen.radar.circles": "Circles: %s", + "civmodern.screen.radar.size": "Size: %s", + "civmodern.screen.radar.alignment": "Alignment: %s", + "civmodern.screen.radar.iconsize": "Icon Size: %s", + "civmodern.screen.radar.range": "Range: %s blocks", + "civmodern.screen.radar.x": "X: %s", + "civmodern.screen.radar.y": "Y: %s", + "civmodern.screen.radar.transparency": "Transparency: %s", + "civmodern.screen.compacted.title": "Civ Modern Config (Compacted Items)", + "civmodern.screen.ice.title": "Civ Modern Config (Ice Road Macro)", + "civmodern.screen.ice.cardinal.enable": "Snap to cardinal: Enabled", + "civmodern.screen.ice.cardinal.disable": "Snap to cardinal: Disabled", + "civmodern.radar.enter" : "§r%s §eappeared at §b[%s %s %s§b]", + "civmodern.radar.hover" : "Click or press %s to highlight position\nControl click to add waypoint", + "civmodern.radar.leave" : "§r%s §edisappeared at §b[%s %s %s§b]" +} \ No newline at end of file diff --git a/common/src/main/resources/civmodern.properties b/common/src/main/resources/civmodern.properties new file mode 100644 index 0000000..2f6497b --- /dev/null +++ b/common/src/main/resources/civmodern.properties @@ -0,0 +1,10 @@ +compacted_colour=16777048 +radar_colour=8947848 +radar_circles=4 +radar_size=50 +alignment=top_left +icon_size=1 +range=64 +radar_enabled=true +ping_enabled=true +ice_road_cardinal=true \ No newline at end of file diff --git a/common/src/main/resources/mixin.civmodern.json b/common/src/main/resources/mixin.civmodern.json new file mode 100644 index 0000000..ba0cc4a --- /dev/null +++ b/common/src/main/resources/mixin.civmodern.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "sh.okx.civmodern.common.mixins", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [ + "InventoryMixin" + ], + "server": [], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle new file mode 100644 index 0000000..f88d319 --- /dev/null +++ b/fabric/build.gradle @@ -0,0 +1,98 @@ +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" +} + +configurations { + shadowCommon +} + +architectury { + platformSetupLoomIde() + fabric() +} + +dependencies { + modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" + // Remove the next line if you don't want to depend on the API + //modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}" + + implementation(project(path: ":common")) { + transitive = false + } + developmentFabric(project(path: ":common")) { + transitive = false + } + shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { + transitive = false + } + minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" + mappings loom.officialMojangMappings() +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +shadowJar { + configurations = [project.configurations.shadowCommon] + classifier "dev-shadow" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar + classifier "fabric" +} + +jar { + classifier "dev" +} + +java { + withSourcesJar() +} + +sourcesJar { + def commonSources = project(":common").sourcesJar + dependsOn commonSources + from commonSources.archiveFile.map { zipTree(it) } +} + +publishing { + publications { + mavenFabric(MavenPublication) { + artifactId = rootProject.archives_base_name + "-" + project.name + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + classifier null + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} + +task cleanJar(type: Delete) { + delete fileTree('../dist') { + include "*-fabric.jar" + } +} + +task copyJar(type: Copy) { + from remapJar + into '../dist' +} + +build.dependsOn copyJar +copyJar.dependsOn cleanJar \ No newline at end of file diff --git a/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernBootstrap.java b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernBootstrap.java new file mode 100644 index 0000000..763d4cd --- /dev/null +++ b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernBootstrap.java @@ -0,0 +1,53 @@ +package sh.okx.civmodern.fabric; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class FabricCivModernBootstrap implements ModInitializer { + private static final Logger LOGGER = LogManager.getLogger(); + + private final FabricCivModernMod mod; + + public FabricCivModernBootstrap() { + this.mod = new FabricCivModernMod(); + } + + @Override + public void onInitialize() { + ClientLifecycleEvents.CLIENT_STARTED.register(e -> mod.enable()); + } + + /*@SubscribeEvent + public void loadWorld(WorldEvent.Load event) { + LOGGER.info("World Load " + event.getPhase() + " " + event.getResult() + " " + event + .getWorld().getClass().getTypeName()); + if (event.getWorld() instanceof ClientWorld) { + BlockModelShapes shaper = Minecraft.getInstance().getBlockRendererDispatcher().getBlockModelShapes(); + + Field cacheField = shaper.getClass().getDeclaredFields()[0]; + Map map; + try { + cacheField.setAccessible(true); + map = new IdentityHashMap<>((Map) cacheField.get(shaper)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return; + } + + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + IBakedModel model = entry.getValue(); + List quads = model + .getQuads(null, Direction.UP, ThreadLocalRandom.current(), + EmptyModelData.INSTANCE); + if (quads.isEmpty()) { + iterator.remove(); + } + } + System.out.println(map.size() + " toal size"); + } + }*/ +} diff --git a/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernMod.java b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernMod.java new file mode 100644 index 0000000..1e56f3e --- /dev/null +++ b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricCivModernMod.java @@ -0,0 +1,19 @@ +package sh.okx.civmodern.fabric; + +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.KeyMapping; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.events.EventBus; + +public class FabricCivModernMod extends AbstractCivModernMod { + + @Override + public EventBus provideEventBus() { + return new FabricEventBus(); + } + + @Override + public void registerKeyBinding(KeyMapping mapping) { + KeyBindingHelper.registerKeyBinding(mapping); + } +} diff --git a/fabric/src/main/java/sh/okx/civmodern/fabric/FabricEventBus.java b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricEventBus.java new file mode 100644 index 0000000..975cd98 --- /dev/null +++ b/fabric/src/main/java/sh/okx/civmodern/fabric/FabricEventBus.java @@ -0,0 +1,43 @@ +package sh.okx.civmodern.fabric; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Consumer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import sh.okx.civmodern.common.events.ClientTickEvent; +import sh.okx.civmodern.common.events.Event; +import sh.okx.civmodern.common.events.EventBus; +import sh.okx.civmodern.common.events.PostRenderGameOverlayEvent; + +public class FabricEventBus implements EventBus { + + private final Map, Set>> map = new ConcurrentHashMap<>(); + + public FabricEventBus() { + map.put(ClientTickEvent.class, new CopyOnWriteArraySet<>()); + map.put(PostRenderGameOverlayEvent.class, new CopyOnWriteArraySet<>()); + + ClientTickEvents.START_CLIENT_TICK.register(client -> push(new ClientTickEvent())); + HudRenderCallback.EVENT.register(((matrixStack, tickDelta) -> push(new PostRenderGameOverlayEvent(matrixStack, tickDelta)))); + } + + private void push(Event event) { + for (Consumer consumer : map.getOrDefault(event.getClass(), Collections.emptySet())) { + consumer.accept(event); + } + } + + @SuppressWarnings("unchecked") + @Override + public void listen(Class event, Consumer listener) { + Set> set = map.get(event); + if (set == null) { + throw new IllegalArgumentException("Class not supported: " + event); + } + set.add((Consumer) listener); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..6af1087 --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "civmodern", + "version": "${version}", + "name": "Civ Modern", + "description": "Civ Modern", + "authors": [ + "Okx" + ], + "license": "Copyright", + "icon": "assets/civmodern/icon.png", + "environment": "client", + "entrypoints": { + "main": [ + "sh.okx.civmodern.fabric.FabricCivModernBootstrap" + ] + }, + "depends": { + "fabricloader": ">=0.7.4", + "fabric": "*", + "minecraft": "1.16.x" + }, + "mixins": [ + "mixin.civmodern.json" + ] +} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle new file mode 100644 index 0000000..670e35a --- /dev/null +++ b/forge/build.gradle @@ -0,0 +1,103 @@ +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" +} + +configurations { + shadowCommon +} + +architectury { + platformSetupLoomIde() + forge() +} + +loom { + useFabricMixin = true +} + +dependencies { + forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}" + // Remove the next line if you don't want to depend on the API + //modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}" + + implementation(project(path: ":common")) { + transitive = false + } + developmentForge(project(path: ":common")) { + transitive = false + } + shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { + transitive = false + } + minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" + mappings loom.officialMojangMappings() +} + +processResources { + inputs.property "version", project.version + + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} + +shadowJar { + exclude "fabric.mod.json" + + configurations = [project.configurations.shadowCommon] + classifier "dev-shadow" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar + classifier "forge" +} + +jar { + classifier "dev" +} + +java { + withSourcesJar() +} + +sourcesJar { + def commonSources = project(":common").sourcesJar + dependsOn commonSources + from commonSources.archiveFile.map { zipTree(it) } +} + +publishing { + publications { + mavenForge(MavenPublication) { + artifactId = rootProject.archives_base_name + "-" + project.name + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + classifier null + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} + +task cleanJar(type: Delete) { + delete fileTree('../dist') { + include "*-forge.jar" + } +} + +task copyJar(type: Copy) { + from remapJar + into '../dist' +} + +build.dependsOn copyJar +copyJar.dependsOn cleanJar \ No newline at end of file diff --git a/forge/gradle.properties b/forge/gradle.properties new file mode 100644 index 0000000..01c185b --- /dev/null +++ b/forge/gradle.properties @@ -0,0 +1 @@ +loom.forge=true \ No newline at end of file diff --git a/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernBootstrap.java b/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernBootstrap.java new file mode 100644 index 0000000..5ce6486 --- /dev/null +++ b/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernBootstrap.java @@ -0,0 +1,25 @@ +package sh.okx.civmodern.forge; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod("civmodern") +public class ForgeCivModernBootstrap { + private static final Logger LOGGER = LogManager.getLogger(); + + private final ForgeCivModernMod mod; + + public ForgeCivModernBootstrap() { + this.mod = new ForgeCivModernMod(); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup); + MinecraftForge.EVENT_BUS.register(this); + } + + public void clientSetup(FMLClientSetupEvent event) { + this.mod.enable(); + } +} diff --git a/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernMod.java b/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernMod.java new file mode 100644 index 0000000..dfb5f65 --- /dev/null +++ b/forge/src/main/java/sh/okx/civmodern/forge/ForgeCivModernMod.java @@ -0,0 +1,18 @@ +package sh.okx.civmodern.forge; + +import net.minecraftforge.fml.client.registry.ClientRegistry; +import sh.okx.civmodern.common.AbstractCivModernMod; +import sh.okx.civmodern.common.events.EventBus; + +public class ForgeCivModernMod extends AbstractCivModernMod { + + @Override + public EventBus provideEventBus() { + return new ForgeEventBus(); + } + + @Override + public void registerKeyBinding(net.minecraft.client.KeyMapping mapping) { + ClientRegistry.registerKeyBinding(mapping); + } +} diff --git a/forge/src/main/java/sh/okx/civmodern/forge/ForgeEventBus.java b/forge/src/main/java/sh/okx/civmodern/forge/ForgeEventBus.java new file mode 100644 index 0000000..df60b0e --- /dev/null +++ b/forge/src/main/java/sh/okx/civmodern/forge/ForgeEventBus.java @@ -0,0 +1,59 @@ +package sh.okx.civmodern.forge; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Consumer; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import sh.okx.civmodern.common.events.ClientTickEvent; +import sh.okx.civmodern.common.events.Event; +import sh.okx.civmodern.common.events.EventBus; +import sh.okx.civmodern.common.events.PostRenderGameOverlayEvent; + +public class ForgeEventBus implements EventBus { + + private final Map, Set>> map = new ConcurrentHashMap<>(); + + public ForgeEventBus() { + map.put(ClientTickEvent.class, new CopyOnWriteArraySet<>()); + map.put(PostRenderGameOverlayEvent.class, new CopyOnWriteArraySet<>()); + + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void onClientTick(TickEvent.ClientTickEvent event) { + if (event.phase == Phase.START) { + push(new ClientTickEvent()); + } + } + + @SubscribeEvent + public void onRender(RenderGameOverlayEvent.Post event) { + if (event.getType() == ElementType.ALL) { + push(new PostRenderGameOverlayEvent(event.getMatrixStack(), event.getPartialTicks())); + } + } + + private void push(Event event) { + for (Consumer consumer : map.getOrDefault(event.getClass(), Collections.emptySet())) { + consumer.accept(event); + } + } + + @Override + public void listen(Class event, Consumer listener) { + Set> set = map.get(event); + if (set == null) { + throw new IllegalArgumentException("Class not supported: " + event); + } + set.add((Consumer) listener); + } +} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..6ef6228 --- /dev/null +++ b/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,28 @@ +modLoader = "javafml" +loaderVersion = "[32,)" +#issueTrackerURL = "" +license = "GPL" + +[[mods]] +modId = "civmodern" +version = "${version}" +displayName = "Civ Modern" +authors = "Me!" +description = ''' +Civ Modern +''' +#logoFile = "" + +[[dependencies.civmodern]] +modId = "forge" +mandatory = true +versionRange = "[32,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.civmodern]] +modId = "minecraft" +mandatory = true +versionRange = "[1.16.1,)" +ordering = "NONE" +side = "CLIENT" diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..06ca035 --- /dev/null +++ b/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Example Mod", + "pack_format": 6 + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..85f1a59 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +org.gradle.jvmargs=-Xmx2048M + +minecraft_version=1.16.5 + +archives_base_name=civmodern +mod_version=1.1.1 +maven_group=sh.okx.civmodern + +architectury_version=1.8.131 + +# https://fabricmc.net/versions.html +fabric_loader_version=0.11.3 +fabric_api_version=0.34.1+1.16 + +forge_version=36.0.46 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2a56324 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5201efb --- /dev/null +++ b/settings.gradle @@ -0,0 +1,14 @@ +pluginManagement { + repositories { + maven { url "https://maven.fabricmc.net/" } + maven { url "https://maven.architectury.dev/" } + maven { url "https://maven.minecraftforge.net/" } + gradlePluginPortal() + } +} + +include("common") +include("fabric") +include("forge") + +rootProject.name = "civmodern"