diff --git a/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java new file mode 100644 index 00000000..7c486c92 --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/Snowflake.java @@ -0,0 +1,31 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.visibleseasons; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Snowflake { + private int x; + private int y; + private int size; + private int speed; +} diff --git a/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java new file mode 100644 index 00000000..7eb8f48f --- /dev/null +++ b/src/main/java/io/github/solclient/client/mod/impl/visibleseasons/VisibleSeasonsMod.java @@ -0,0 +1,61 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.mod.impl.visibleseasons; + +import com.google.gson.annotations.Expose; +import io.github.solclient.client.mod.impl.StandardMod; +import io.github.solclient.client.mod.option.annotation.Option; +import io.github.solclient.client.mod.option.annotation.Slider; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.language.I18n; + +import java.util.ArrayList; +import java.util.List; + +public class VisibleSeasonsMod extends StandardMod { + public static VisibleSeasonsMod instance; + protected final MinecraftClient mc = MinecraftClient.getInstance(); + public List snowflakes = new ArrayList<>(); + + @Expose + @Option + public boolean forceVisibleSeasons; + + @Expose + @Option + @Slider(min = 1, max = 50, step = 1) + public float visibleSeasonsAmount = 25; + + @Expose + @Option + public boolean visibleSeasonsLowDetail; + + + @Override + public String getDetail() { + return I18n.translate("sol_client.mod.screen.by", "ArikSquad"); + } + + @Override + public void init() { + super.init(); + instance = this; + } + +} diff --git a/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java b/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java index f6ee9a29..2d8ae765 100644 --- a/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java +++ b/src/main/java/io/github/solclient/client/ui/component/ComponentScreen.java @@ -18,18 +18,30 @@ package io.github.solclient.client.ui.component; -import org.apache.logging.log4j.*; -import org.lwjgl.LWJGLException; -import org.lwjgl.input.*; - +import io.github.solclient.client.mod.impl.visibleseasons.Snowflake; +import io.github.solclient.client.mod.impl.visibleseasons.VisibleSeasonsMod; import io.github.solclient.client.ui.component.controller.ParentBoundsController; -import io.github.solclient.client.util.*; -import io.github.solclient.client.util.cursors.SystemCursors; -import io.github.solclient.client.util.data.*; +import io.github.solclient.client.util.MinecraftUtils; +import io.github.solclient.client.util.NanoVGManager; +import io.github.solclient.client.util.data.Colour; +import io.github.solclient.client.util.data.Rectangle; import lombok.Getter; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.util.Window; +import net.minecraft.util.Identifier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.nanovg.NVGPaint; +import org.lwjgl.nanovg.NanoVG; + +import java.time.LocalDate; +import java.time.Month; +import java.util.Iterator; +import java.util.concurrent.ThreadLocalRandom; public class ComponentScreen extends Screen { @@ -41,6 +53,9 @@ public class ComponentScreen extends Screen { protected boolean background = true; private float mouseX, mouseY; + private long lastFrameTime = 0; + private final long nvg; + public ComponentScreen(Component root) { this.parentScreen = MinecraftClient.getInstance().currentScreen; rootWrapper = new Component() { @@ -53,6 +68,8 @@ public Rectangle getBounds() { }; rootWrapper.setScreen(this); + nvg = NanoVGManager.getNvg(); + rootWrapper.add(root, new ParentBoundsController()); this.root = root; } @@ -63,6 +80,46 @@ public Component getRoot() { @Override public void render(int mouseX, int mouseY, float tickDelta) { + if (VisibleSeasonsMod.instance.forceVisibleSeasons || LocalDate.now().getMonth() == Month.DECEMBER) { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastFrameTime >= ThreadLocalRandom.current().nextInt(400)) { + if (VisibleSeasonsMod.instance.snowflakes.size() < (int) VisibleSeasonsMod.instance.visibleSeasonsAmount) { + int snowflakeX = ThreadLocalRandom.current().nextInt(client.width); + int snowflakeSize = 5 + ThreadLocalRandom.current().nextInt(6); + int snowflakeSpeed = 1 + ThreadLocalRandom.current().nextInt(3); + VisibleSeasonsMod.instance.snowflakes.add(new Snowflake(snowflakeX, 0, snowflakeSize, snowflakeSpeed)); + lastFrameTime = currentTime; + } + } + + + Iterator iterator = VisibleSeasonsMod.instance.snowflakes.iterator(); + while (iterator.hasNext()) { + Snowflake snowflake = iterator.next(); + int x = snowflake.getX(); + int y = snowflake.getY(); + int size = snowflake.getSize(); + + NanoVG.nvgBeginPath(nvg); + if (VisibleSeasonsMod.instance.visibleSeasonsLowDetail) { + NanoVG.nvgRect(nvg, x, y, size, size); + NanoVG.nvgFillColor(nvg, Colour.WHITE.nvg()); + NanoVG.nvgFill(nvg); + } else { + NVGPaint paint = MinecraftUtils.nvgMinecraftTexturePaint(nvg, new Identifier("sol_client", "textures/gui/snowflake.png"), x, y, size, size, 0); + NanoVG.nvgFillPaint(nvg, paint); + NanoVG.nvgFill(nvg); + } + + // Reset the snowflake if it goes beyond the screen bounds + if (y > client.height) { + iterator.remove(); + } else { + snowflake.setY(y + snowflake.getSpeed()); + } + } + } + try { Window window = new Window(client); diff --git a/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java b/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java index d6f498bd..c712b446 100644 --- a/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java +++ b/src/main/java/io/github/solclient/client/ui/screen/mods/ModsScreen.java @@ -18,23 +18,35 @@ package io.github.solclient.client.ui.screen.mods; -import org.lwjgl.input.Keyboard; - import io.github.solclient.client.SolClient; -import io.github.solclient.client.mod.*; +import io.github.solclient.client.mod.Mod; +import io.github.solclient.client.mod.ModUiStateManager; import io.github.solclient.client.mod.impl.core.CoreMod; -import io.github.solclient.client.ui.*; -import io.github.solclient.client.ui.component.*; -import io.github.solclient.client.ui.component.controller.*; -import io.github.solclient.client.ui.component.impl.*; +import io.github.solclient.client.ui.ScreenAnimation; +import io.github.solclient.client.ui.Theme; +import io.github.solclient.client.ui.component.Component; +import io.github.solclient.client.ui.component.ComponentRenderInfo; +import io.github.solclient.client.ui.component.controller.AlignedBoundsController; +import io.github.solclient.client.ui.component.controller.Controller; +import io.github.solclient.client.ui.component.impl.BlockComponent; +import io.github.solclient.client.ui.component.impl.ButtonComponent; +import io.github.solclient.client.ui.component.impl.LabelComponent; +import io.github.solclient.client.ui.component.impl.TextFieldComponent; import io.github.solclient.client.ui.screen.PanoramaBackgroundScreen; -import io.github.solclient.client.util.*; -import io.github.solclient.client.util.data.*; +import io.github.solclient.client.util.ActiveMainMenu; +import io.github.solclient.client.util.KeyBindingInterface; +import io.github.solclient.client.util.MinecraftUtils; +import io.github.solclient.client.util.data.Alignment; +import io.github.solclient.client.util.data.Rectangle; import lombok.Getter; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.resource.language.I18n; +import org.lwjgl.input.Keyboard; + public class ModsScreen extends PanoramaBackgroundScreen { + protected MinecraftClient mc = MinecraftClient.getInstance(); private final ModsScreenComponent component; private final ScreenAnimation animation = new ScreenAnimation(); @@ -280,8 +292,25 @@ public boolean mouseClickedAnywhere(ComponentRenderInfo info, int button, boolea return super.mouseClickedAnywhere(info, button, inside, processed); } + public static final int[] keys = { + Keyboard.KEY_UP, Keyboard.KEY_UP, Keyboard.KEY_DOWN, Keyboard.KEY_DOWN, Keyboard.KEY_LEFT, Keyboard.KEY_RIGHT, + Keyboard.KEY_LEFT, Keyboard.KEY_RIGHT, Keyboard.KEY_B, Keyboard.KEY_A + }; + + private int keyIndex = 0; + @Override public boolean keyPressed(ComponentRenderInfo info, int keyCode, char character) { + if (keyCode == keys[keyIndex]) { + keyIndex++; + if (keyIndex == keys.length) { + mc.setScreen(new SnakeScreen()); + keyIndex = 0; + } + } else { + keyIndex = 0; + } + if ((screen.getRoot().getDialog() == null && (keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER))) { if (mod == null) { diff --git a/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java b/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java new file mode 100644 index 00000000..25fd0eb2 --- /dev/null +++ b/src/main/java/io/github/solclient/client/ui/screen/mods/SnakeScreen.java @@ -0,0 +1,271 @@ +/* + * Sol Client - an open source Minecraft client + * Copyright (C) 2021-2023 TheKodeToad and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.solclient.client.ui.screen.mods; + +import io.github.solclient.client.SolClient; +import io.github.solclient.client.mod.impl.visibleseasons.VisibleSeasonsMod; +import io.github.solclient.client.mod.impl.core.CoreMod; +import io.github.solclient.client.ui.ScreenAnimation; +import io.github.solclient.client.ui.component.Component; +import io.github.solclient.client.ui.component.ComponentRenderInfo; +import io.github.solclient.client.ui.component.controller.AlignedBoundsController; +import io.github.solclient.client.ui.component.impl.BlockComponent; +import io.github.solclient.client.ui.component.impl.LabelComponent; +import io.github.solclient.client.ui.screen.PanoramaBackgroundScreen; +import io.github.solclient.client.util.ActiveMainMenu; +import io.github.solclient.client.util.data.Alignment; +import io.github.solclient.client.util.data.Colour; +import io.github.solclient.client.util.data.Rectangle; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.input.Keyboard; +import org.lwjgl.nanovg.NanoVG; + +import java.util.ArrayList; +import java.util.List; + + +public class SnakeScreen extends PanoramaBackgroundScreen { + + protected MinecraftClient mc = MinecraftClient.getInstance(); + private final ScreenAnimation animation = new ScreenAnimation(); + + public SnakeScreen() { + super(new Component() { + { + add(new SnakeComponent(), new AlignedBoundsController(Alignment.CENTRE, Alignment.CENTRE)); + } + }); + + background = false; + } + + @Override + public void init() { + super.init(); + Keyboard.enableRepeatEvents(true); + } + + @Override + public void render(int mouseX, int mouseY, float tickDelta) { + if (client.world == null) { + if (CoreMod.instance.fancyMainMenu) { + background = false; + drawPanorama(mouseX, mouseY, tickDelta); + } else + background = true; + } + + super.render(mouseX, mouseY, tickDelta); + } + + @Override + protected void wrap(Runnable task) { + animation.wrap(task); + } + + @Override + public void removed() { + super.removed(); + animation.close(); + SolClient.INSTANCE.saveAll(); + Keyboard.enableRepeatEvents(false); + } + + @Override + public void closeAll() { + if (client.world == null && CoreMod.instance.fancyMainMenu) { + client.setScreen(ActiveMainMenu.getInstance()); + return; + } + + super.closeAll(); + } + + public static class SnakeComponent extends BlockComponent { + + public static final int[] keys = {Keyboard.KEY_S, Keyboard.KEY_E, Keyboard.KEY_A, Keyboard.KEY_S, Keyboard.KEY_O, Keyboard.KEY_N, Keyboard.KEY_S}; + private int keyIndex = 0; + + private int foodX = 0; + private int foodY = 0; + + private int snakeLength = 1; + private final int snakeSize = 10; + private final List snakeX = new ArrayList<>(); + private final List snakeY = new ArrayList<>(); + private int snakeDirection = 3; // 0 = up, 1 = right, 2 = down, 3 = left + + private long lastFrameTime = 0; + private final List nextMoves = new ArrayList<>(); + + private final int screenSize = 500; + + public SnakeComponent() { + super(new Colour(0xFF202020)); + add(new LabelComponent((component, defaultText) -> String.valueOf(snakeLength - 3)).scaled(1.45F), new AlignedBoundsController(Alignment.CENTRE, Alignment.START)); + + resetSnake(); + generateFood(); + } + + @Override + public void render(ComponentRenderInfo info) { + super.render(info); + + renderSnake(); + renderFood(); + + if (isCollidingFood()) { + generateFood(); + addFood(); + } + + long currentTime = System.currentTimeMillis(); + if (currentTime - lastFrameTime >= 50) { + moveSnake(); + lastFrameTime = currentTime; + } + } + + private void addFood() { + snakeLength++; + int lastIndex = snakeLength - 1; + snakeX.add(snakeX.get(lastIndex - 1)); + snakeY.add(snakeY.get(lastIndex - 1)); + } + + private boolean isCollidingFood() { + return snakeX.get(0) == foodX && snakeY.get(0) == foodY; + } + + private void generateFood() { + foodX = (int) (Math.random() * screenSize); + foodY = (int) (Math.random() * screenSize); + foodX = foodX - (foodX % snakeSize); + foodY = foodY - (foodY % snakeSize); + } + + private void moveSnake() { + if (nextMoves.size() > 0) { + changeDirection(nextMoves.get(0)); + nextMoves.remove(0); + } + + for (int i = snakeLength - 1; i > 0; i--) { + snakeX.set(i, snakeX.get(i - 1)); + snakeY.set(i, snakeY.get(i - 1)); + } + + if (snakeDirection == 0) { + snakeY.set(0, snakeY.get(0) - snakeSize); + } else if (snakeDirection == 1) { + snakeX.set(0, snakeX.get(0) + snakeSize); + } else if (snakeDirection == 2) { + snakeY.set(0, snakeY.get(0) + snakeSize); + } else if (snakeDirection == 3) { + snakeX.set(0, snakeX.get(0) - snakeSize); + } + + // Check if the new head position is within bounds + if (snakeX.get(0) < 0 || snakeX.get(0) >= screenSize || snakeY.get(0) < 0 || snakeY.get(0) >= screenSize) { + resetSnake(); + } + } + + + private void renderSnake() { + for (int i = 0; i < snakeLength; i++) { + if (i == 0) { + NanoVG.nvgBeginPath(nvg); + NanoVG.nvgRect(nvg, snakeX.get(i), snakeY.get(i), snakeSize, snakeSize); + NanoVG.nvgFillColor(nvg, new Colour(0xFF5dd95b).nvg()); + NanoVG.nvgFill(nvg); + } else { + NanoVG.nvgBeginPath(nvg); + NanoVG.nvgRect(nvg, snakeX.get(i), snakeY.get(i), snakeSize, snakeSize); + NanoVG.nvgFillColor(nvg, new Colour(0xFF69ff67).nvg()); + NanoVG.nvgFill(nvg); + } + } + } + + private void renderFood() { + NanoVG.nvgBeginPath(nvg); + NanoVG.nvgRect(nvg, foodX, foodY, snakeSize, snakeSize); + NanoVG.nvgFillColor(nvg, new Colour(0xFFfe6666).nvg()); + NanoVG.nvgFill(nvg); + } + + private void resetSnake() { + snakeLength = 3; + snakeDirection = 3; + snakeX.clear(); + snakeY.clear(); + + for (int i = 0; i < snakeLength; i++) { + snakeX.add((screenSize / 2) - (i * snakeSize)); + snakeY.add(screenSize / 2); + } + + moveSnake(); + } + + public void changeDirection(int newDirection) { + // Prevent the snake from immediately reversing its direction + if (Math.abs(snakeDirection - newDirection) != 2) { + snakeDirection = newDirection; + } + } + + @Override + public boolean keyPressed(ComponentRenderInfo info, int keyCode, char character) { + if (keyCode == keys[keyIndex]) { + keyIndex++; + if (keyIndex == keys.length) { + mc.setScreen(new ModsScreen(VisibleSeasonsMod.instance)); + keyIndex = 0; + } + } else { + keyIndex = 0; + } + + if (keyCode == Keyboard.KEY_UP) { + nextMoves.add(0); + } + if (keyCode == Keyboard.KEY_RIGHT) { + nextMoves.add(1); + } + if (keyCode == Keyboard.KEY_DOWN) { + nextMoves.add(2); + } + if (keyCode == Keyboard.KEY_LEFT) { + nextMoves.add(3); + } + + return super.keyPressed(info, keyCode, character); + } + + @Override + public Rectangle getDefaultBounds() { + return Rectangle.ofDimensions(screenSize, screenSize); + } + + } + +} diff --git a/src/main/resources/assets/sol_client/lang/en_US.lang b/src/main/resources/assets/sol_client/lang/en_US.lang index dd990724..a410612c 100644 --- a/src/main/resources/assets/sol_client/lang/en_US.lang +++ b/src/main/resources/assets/sol_client/lang/en_US.lang @@ -427,6 +427,12 @@ sol_client.mod.screenshots.deleted=The screenshot was deleted sol_client.mod.chat_hotkeys.name=Chat Hotkeys sol_client.mod.chat_hotkeys.description=Bind commands and messages to keys. +sol_client.mod.visible_seasons.name=Visible Seasons +sol_client.mod.visible_seasons.description=Render season decorations in the module menu. +sol_client.mod.visible_seasons.option.forceVisibleSeasons=Force season decorations +sol_client.mod.visible_seasons.option.visibleSeasonsAmount=Amount of decorations +sol_client.mod.visible_seasons.option.visibleSeasonsLowDetail=Low detail mode + sol_client.mod.chat_hotkeys.option.hotkeys=Hotkeys sol_client.mod.chat_hotkeys.option.cooldown=Rate limit diff --git a/src/main/resources/assets/sol_client/lang/fi_FI.lang b/src/main/resources/assets/sol_client/lang/fi_FI.lang index 3e162b5b..7068fb04 100644 --- a/src/main/resources/assets/sol_client/lang/fi_FI.lang +++ b/src/main/resources/assets/sol_client/lang/fi_FI.lang @@ -494,3 +494,9 @@ sol_client.mod.toggle_sprint.sneak_toggled=Hiipiminen (kytketty) sol_client.mod.screenshots.delete=Poista sol_client.mod.screenshots.delete_error=Poiston aikana tapahtui virhe: %s. Sinä voit yrittää avata %s sen sijaan. sol_client.slider.pixels=%spx + +sol_client.mod.visible_seasons.name=Näkyvät vuodenajat +sol_client.mod.visible_seasons.description=Näytä vuodenaikaan liittyvät koristeet moduulivalikossa. +sol_client.mod.visible_seasons.option.forceVisibleSeasons=Pakota vuodenaika koristeet +sol_client.mod.visible_seasons.option.visibleSeasonsAmount=Koristeiden määrä +sol_client.mod.visible_seasons.option.visibleSeasonsLowDetail=Alhainen yksityiskohtaisuus diff --git a/src/main/resources/assets/sol_client/textures/gui/snowflake.png b/src/main/resources/assets/sol_client/textures/gui/snowflake.png new file mode 100644 index 00000000..64dfebe4 Binary files /dev/null and b/src/main/resources/assets/sol_client/textures/gui/snowflake.png differ diff --git a/src/main/resources/standard-mods.json b/src/main/resources/standard-mods.json index e285cb70..fff0029c 100644 --- a/src/main/resources/standard-mods.json +++ b/src/main/resources/standard-mods.json @@ -255,6 +255,12 @@ "main": "HitColourMod", "category": "visual" }, + { + "id": "visible_seasons", + "main": "visibleseasons.VisibleSeasonsMod", + "category": "hidden", + "forcedOn": true + }, // integration { "id": "hypixel_util",