├── .github └── workflows │ ├── build.yml │ └── upload-sources-to-crowdin.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── java │ └── ca │ └── tirelesstraveler │ ├── DownloadTranslationsTask.kt │ └── UploadTranslationsTask.kt ├── crowdin.yml ├── docs ├── Fancy Warp Menu Large.png ├── Fancy Warp Menu Normal.png └── Settings.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── log4j2.xml ├── settings.gradle.kts ├── src └── main │ ├── java │ └── ca │ │ └── tirelesstraveler │ │ └── fancywarpmenu │ │ ├── FancyWarpMenu.java │ │ ├── LogHelper.java │ │ ├── commands │ │ └── FancyWarpMenuCommand.java │ │ ├── data │ │ ├── Settings.java │ │ ├── layout │ │ │ ├── Button.java │ │ │ ├── ConfigButton.java │ │ │ ├── Island.java │ │ │ ├── Layout.java │ │ │ ├── RegularWarpMenuButton.java │ │ │ ├── Warp.java │ │ │ └── WarpIcon.java │ │ └── skyblockconstants │ │ │ ├── SkyBlockConstants.java │ │ │ ├── WarpCommandVariant.java │ │ │ ├── WarpMessages.java │ │ │ └── menu │ │ │ ├── ItemMatchCondition.java │ │ │ └── Menu.java │ │ ├── gui │ │ ├── FancyWarpMenuConfigScreen.java │ │ ├── FancyWarpMenuGuiFactory.java │ │ ├── GuiChestMenu.java │ │ ├── GuiFancyWarp.java │ │ ├── GuiFastTravel.java │ │ ├── GuiRiftFastTravel.java │ │ ├── buttons │ │ │ ├── GuiButtonChestMenu.java │ │ │ ├── GuiButtonConfig.java │ │ │ ├── GuiButtonIsland.java │ │ │ ├── GuiButtonRegularWarpMenu.java │ │ │ ├── GuiButtonScaleTransition.java │ │ │ ├── GuiButtonTimedLabel.java │ │ │ └── GuiButtonWarp.java │ │ ├── grid │ │ │ ├── GridRectangle.java │ │ │ └── ScaledGrid.java │ │ └── transitions │ │ │ ├── ScaleTransition.java │ │ │ └── Transition.java │ │ ├── listeners │ │ ├── ChatListener.java │ │ ├── InventoryChangeListener.java │ │ ├── SkyBlockJoinListener.java │ │ └── WarpMenuListener.java │ │ ├── resourceloaders │ │ ├── LayoutLoader.java │ │ ├── ResourceLoader.java │ │ └── SkyBlockConstantsLoader.java │ │ ├── state │ │ ├── EnvironmentDetails.java │ │ ├── FancyWarpMenuState.java │ │ └── GameState.java │ │ └── utils │ │ ├── ChatUtils.java │ │ ├── GameChecks.java │ │ └── WarpVisibilityChecks.java │ └── resources │ ├── assets │ └── fancywarpmenu │ │ ├── data │ │ ├── layout.json │ │ ├── riftLayout.json │ │ └── skyBlockConstants.jsonc │ │ ├── lang │ │ └── en_US.lang │ │ └── textures │ │ └── gui │ │ ├── Logo.png │ │ ├── Notification.png │ │ ├── Portal.png │ │ ├── Regular Warp Menu.png │ │ └── islands │ │ ├── Barn.png │ │ ├── Crimson Isle.png │ │ ├── Deep Caverns.png │ │ ├── Dungeon Hub.png │ │ ├── End.png │ │ ├── Garden.png │ │ ├── Gold Mine.png │ │ ├── Hub.png │ │ ├── Jerry's Workshop.png │ │ ├── Mushroom Desert.png │ │ ├── Park.png │ │ ├── Private Island.png │ │ ├── Rift.png │ │ └── Spider's Den.png │ ├── mcmod.info │ └── version.properties └── version ├── README.md └── update.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '**.java' 9 | - 'src/main/resources/**' 10 | pull_request: 11 | branches: 12 | - main 13 | paths: 14 | - '**.java' 15 | - 'src/main/resources/**' 16 | workflow_dispatch: 17 | inputs: 18 | upload_artifact: 19 | description: 'Upload artifact to this run? [y | n]' 20 | required: false 21 | default: 'n' 22 | send_notification: 23 | description: 'Send a notification to Discord after this run finishes successfully? [y | n]' 24 | required: false 25 | default: 'n' 26 | jobs: 27 | build: 28 | name: Build 29 | runs-on: ubuntu-latest 30 | timeout-minutes: 15 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | fetch-depth: 20 36 | - uses: actions/setup-java@v4 37 | with: 38 | distribution: 'temurin' 39 | java-version: '17' 40 | - name: Setup Gradle 41 | uses: gradle/actions/setup-gradle@v3 42 | - name: Download translations from Crowdin 43 | uses: crowdin/github-action@v1 44 | with: 45 | skip_ref_checkout: true 46 | download_translations: true 47 | export_only_approved: true 48 | push_translations: false 49 | upload_sources: false 50 | env: 51 | CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} 52 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} 53 | - name: Fix ownership of Gradle build directory 54 | run: '[ -d build ] && sudo chown -R $(whoami):$(id -gn) build || echo "Skipped, build directory does not exist."' 55 | - name: Build with Gradle 56 | run: gradle build --no-daemon 57 | - name: Read Project Version 58 | id: read_project_version 59 | if: ${{ github.ref_name == 'main' || contains(github.event.inputs.upload_artifact, 'y') }} 60 | uses: christian-draeger/read-properties@1.1.1 61 | with: 62 | path: gradle.properties 63 | properties: version 64 | - name: Upload Artifact 65 | if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' || contains(github.event.inputs.upload_artifact, 'y') }} 66 | uses: actions/upload-artifact@v3 67 | with: 68 | name: Fancy Warp Menu 69 | path: build/libs/* 70 | - name: Get Pushed Commit Messages 71 | if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} 72 | run: | 73 | EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) 74 | echo "COMMIT_LOG<<$EOF" >> "$GITHUB_ENV" 75 | echo "$(git log -n 20 --format=format:"%aD%n%B" ${{ github.event.before }}..${{ github.event.after }})" >> "$GITHUB_ENV" 76 | echo "$EOF" >> "$GITHUB_ENV" 77 | - name: Get Head Commit Message 78 | if: ${{ env.COMMIT_LOG == '' }} 79 | run: | 80 | EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) 81 | echo "COMMIT_LOG<<$EOF" >> "$GITHUB_ENV" 82 | echo "$(git show -s --format=format:"%B")" >> "$GITHUB_ENV" 83 | echo "$EOF" >> "$GITHUB_ENV" 84 | - name: Post Build Information to Discord 85 | if: ${{ (github.event_name == 'push' && github.ref_name == 'main') || github.event_name == 'workflow_dispatch' && contains(github.event.inputs.send_notification, 'y') }} 86 | env: 87 | jarPath: build/libs/FancyWarpMenu-${{ steps.read_project_version.outputs.version }}+${{ github.run_number }}.jar 88 | uses: tsickert/discord-webhook@v5.3.0 89 | with: 90 | webhook-url: ${{ secrets.BUILD_WEBHOOK_URL }} 91 | # New build notifications role 92 | content: <@&1124879006597140481> 93 | embed-title: ${{ steps.read_project_version.outputs.version }} Build \#${{ github.run_number }} 94 | embed-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/ 95 | embed-description: | 96 | ${{ env.COMMIT_LOG }} 97 | 98 | [File SHA-256](https://emn178.github.io/online-tools/sha256_checksum.html 'Drop jar onto linked site and verify hash matches the one below.') below ([what's this?](https://github.com/BiscuitDevelopment/SkyblockAddons/wiki/How-to-Verify-the-File-Hash)) 99 | embed-footer-text: ${{ hashFiles(env.jarPath) }} 100 | filename: ${{ env.jarPath }} 101 | -------------------------------------------------------------------------------- /.github/workflows/upload-sources-to-crowdin.yml: -------------------------------------------------------------------------------- 1 | name: Upload Sources to Crowdin 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/main/resources/lang/en_US.lang' 9 | jobs: 10 | upload: 11 | name: Upload Sources to Crowdin 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Upload 15 | uses: crowdin/github-action@v1 16 | env: 17 | CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} 18 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | run/ 4 | build/ 5 | .gradle/ 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023. TirelessTraveler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build](https://github.com/ILikePlayingGames/FancyWarpMenu/actions/workflows/build.yml/badge.svg)](https://github.com/ILikePlayingGames/FancyWarpMenu/actions/workflows/build.yml) 2 | [![Discord](https://img.shields.io/discord/1122365627751989329?logo=discord)](https://discord.gg/tXFf9umfA9) 3 | [![Crowdin](https://badges.crowdin.net/fancy-warp-menu/localized.svg)](https://crowdin.com/project/fancy-warp-menu) 4 | 5 | # Fancy Warp Menu 6 | 7 | A prettier warp menu for Hypixel SkyBlock 8 | 9 | ----- 10 | 11 | This is a recreation of the fancy warp menu from SkyblockAddons, rewritten for better performance. 12 | 13 | ## Features 14 | 15 | - Built for Forge for Minecraft 1.8.9 16 | - Fabric 1.20.1 port planned 17 | - Lightweight 18 | - Just the warp menu, no additional features 19 | - Supports custom layouts 20 | - Learn to make one [here](https://github.com/ILikePlayingGames/FancyWarpPackTemplate)! 21 | 22 | ## Screenshots 23 | 24 | ![Fancy Warp Menu at large GUI scale](docs/Fancy%20Warp%20Menu%20Large.png) 25 | Large GUI scale 26 | 27 | 28 | ![Fancy Warp Menu at normal GUI scale](docs/Fancy%20Warp%20Menu%20Large.png) 29 | Normal GUI scale 30 | 31 | 32 | ![Fancy Warp Menu Settings](docs/Settings.png) 33 | Settings (access by clicking the portal in the bottom right corner) 34 | 35 | ## Supported Languages 36 | 37 | Help us translate the mod on [Crowdin](https://crowdin.com/project/fancy-warp-menu)! 38 | - English (US) 39 | - Chinese Simplified 40 | - Chinese Traditional 41 | - French 42 | - German 43 | 44 | ## Support 45 | 46 | Please join my [Discord server](https://discord.gg/tXFf9umfA9) for support. 47 | 48 | ## Credits 49 | 50 | Mod template: [Nea's Forge 1.8.9 Template](https://github.com/romangraef/Forge1.8.9Template) 51 |
52 | Portal texture: [SkyblockAddons](https://github.com/BiscuitDevelopment/SkyblockAddons/blob/main/src/main/resources/assets/skyblockaddons/portal.png) 53 |
54 | Island Renders: Rendered by Schlaumeyer using [Blender](https://www.blender.org/) 55 |
56 | Hypixel SkyBlock and SkyBlock Islands: Hypixel Inc. 57 |
58 | Minecraft block textures: Mojang 59 |
60 | Translators: 61 | - PumpkinXD 62 | - Rodry 63 | - Astri_ 64 | - KoT_B_MeWkE 65 | - outsparkled 66 | - taku565758 67 | - larryshow 68 | - The_Deerman 69 | - Seeloewen 70 |
71 |
72 | Fancy Warp Menu is a modification created by players and is not affiliated with or endorsed by Hypixel Inc. 73 |
74 | NOT AN OFFICIAL MINECRAFT PRODUCT. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT. 75 | 76 | ## Licenses 77 | 78 | View the list [here](https://github.com/ILikePlayingGames/FancyWarpMenu/wiki/Licenses). 79 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | import ca.tirelesstraveler.DownloadTranslationsTask 4 | import ca.tirelesstraveler.UploadTranslationsTask 5 | import org.apache.commons.lang3.SystemUtils 6 | 7 | plugins { 8 | idea 9 | java 10 | id("gg.essential.loom") version "0.10.0.+" 11 | id("dev.architectury.architectury-pack200") version "0.1.3" 12 | id("com.github.johnrengelman.shadow") version "8.1.1" 13 | } 14 | 15 | //Constants: 16 | 17 | val baseGroup: String by project 18 | val mcVersion: String by project 19 | val modid: String by project 20 | val version: String by project 21 | 22 | // Toolchains: 23 | java { 24 | toolchain.languageVersion.set(JavaLanguageVersion.of(8)) 25 | } 26 | 27 | // Minecraft configuration: 28 | loom { 29 | log4jConfigs.from(file("log4j2.xml")) 30 | launchConfigs { 31 | "client" { 32 | property("devauth.enabled", "true") 33 | } 34 | } 35 | runConfigs { 36 | "client" { 37 | if (SystemUtils.IS_OS_MAC_OSX) { 38 | // This argument causes a crash on macOS 39 | vmArgs.remove("-XstartOnFirstThread") 40 | } 41 | } 42 | remove(getByName("server")) 43 | create("client21by9") { 44 | name("Minecraft Client 21:9") 45 | environment("client") 46 | defaultMainClass(getByName("client").defaultMainClass) 47 | programArgs("--width", "1920", "--height", "823") 48 | } 49 | create("client32by9") { 50 | name("Minecraft Client 32:9") 51 | environment("client") 52 | defaultMainClass(getByName("client").defaultMainClass) 53 | programArgs("--width", "1920", "--height", "540") 54 | } 55 | } 56 | forge { 57 | pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) 58 | } 59 | } 60 | 61 | // Dependencies: 62 | 63 | repositories { 64 | mavenCentral() 65 | maven("https://repo.spongepowered.org/maven/") 66 | maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") 67 | } 68 | 69 | val shadowImpl: Configuration by configurations.creating { 70 | configurations.implementation.get().extendsFrom(this) 71 | } 72 | 73 | dependencies { 74 | minecraft("com.mojang:minecraft:1.8.9") 75 | mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") 76 | forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") 77 | runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.0") 78 | } 79 | 80 | // Tasks: 81 | 82 | tasks.withType(JavaCompile::class) { 83 | options.encoding = "UTF-8" 84 | } 85 | 86 | tasks.withType(Jar::class) { 87 | archiveBaseName.set(modid) 88 | manifest.attributes.run { 89 | this["FMLCorePluginContainsFMLMod"] = "true" 90 | this["ForceLoadAsMod"] = "true" 91 | } 92 | } 93 | 94 | tasks.processResources { 95 | if (System.getenv("CI") != null) { 96 | if (System.getenv("GITHUB_RUN_NUMBER") != null) { 97 | project.version = "${project.version}+${System.getenv("GITHUB_RUN_NUMBER")}" 98 | } else { 99 | throw RuntimeException("Environment variable GITHUB_RUN_NUMBER missing on CI build") 100 | } 101 | } 102 | 103 | inputs.property("version", project.version) 104 | inputs.property("mcversion", mcVersion) 105 | inputs.property("modid", modid) 106 | 107 | filesMatching(arrayListOf("mcmod.info", "version.properties")) { 108 | expand(inputs.properties) 109 | } 110 | 111 | rename("(.+_at.cfg)", "META-INF/$1") 112 | } 113 | 114 | 115 | val remapJar by tasks.named("remapJar") { 116 | archiveClassifier.set("") 117 | from(tasks.shadowJar) 118 | input.set(tasks.shadowJar.get().archiveFile) 119 | } 120 | 121 | tasks.jar { 122 | archiveClassifier.set("without-deps") 123 | destinationDirectory.set(layout.buildDirectory.dir("badjars")) 124 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 125 | } 126 | 127 | tasks.shadowJar { 128 | destinationDirectory.set(layout.buildDirectory.dir("badjars")) 129 | archiveClassifier.set("all-dev") 130 | configurations = listOf(shadowImpl) 131 | doLast { 132 | configurations.forEach { 133 | println("Copying jars into mod: ${it.files}") 134 | } 135 | } 136 | 137 | // If you want to include other dependencies and shadow them, you can relocate them in here 138 | // fun relocate(name: String) = relocate(name, "$baseGroup.deps.$name") 139 | } 140 | 141 | tasks.register("downloadTranslations") { 142 | group = "translations" 143 | exportOnlyApproved = true 144 | } 145 | tasks.register("uploadTranslations") { 146 | group = "translations" 147 | } 148 | 149 | tasks.register("copyTranslationsToClassesDirectory") { 150 | group = "translations" 151 | from(layout.buildDirectory.dir("generated/resources/crowdin")) 152 | into(sourceSets.main.get().java.classesDirectory.get()) 153 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 154 | } 155 | 156 | /** 157 | * Copy built jar into a Minecraft launcher instance for debugging in a production environment 158 | */ 159 | tasks.register("copyJarToMinecraftLauncher") { 160 | from(layout.buildDirectory.dir("libs")) 161 | into(file(System.getenv("MC_LAUNCHER_DIR"))) 162 | } 163 | 164 | tasks.assemble.get().dependsOn(tasks.remapJar) 165 | 166 | sourceSets.main { 167 | output.setResourcesDir(sourceSets.main.flatMap { it.java.classesDirectory }) 168 | output.dir(layout.buildDirectory.dir("generated/resources/crowdin")) 169 | } -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | plugins { 24 | kotlin("jvm") version "1.9.10" 25 | } 26 | 27 | group = "ca.tirelesstraveler" 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | kotlin { 34 | jvmToolchain(17) 35 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/ca/tirelesstraveler/DownloadTranslationsTask.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler 24 | 25 | import org.gradle.api.DefaultTask 26 | import org.gradle.api.file.ProjectLayout 27 | import org.gradle.api.provider.Property 28 | import org.gradle.api.tasks.Input 29 | import org.gradle.api.tasks.TaskAction 30 | import org.gradle.process.ExecOperations 31 | import java.io.ByteArrayOutputStream 32 | import javax.inject.Inject 33 | 34 | /* 35 | * Copyright (c) 2023. TirelessTraveler 36 | * 37 | * Permission is hereby granted, free of charge, to any person obtaining a copy 38 | * of this software and associated documentation files (the "Software"), to deal 39 | * in the Software without restriction, including without limitation the rights 40 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | * copies of the Software, and to permit persons to whom the Software is 42 | * furnished to do so, subject to the following conditions: 43 | * 44 | * The above copyright notice and this permission notice shall be included in all 45 | * copies or substantial portions of the Software. 46 | * 47 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 50 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 51 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 52 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 53 | * OR OTHER DEALINGS IN THE SOFTWARE. 54 | */ 55 | 56 | /** 57 | * This task uses the Crowdin CLI to download translations to the folder configured in crowdin.yml. 58 | * It is incremental and will re-run when the Crowdin project's translation progress changes. 59 | */ 60 | abstract class DownloadTranslationsTask: DefaultTask() { 61 | @Inject 62 | protected abstract fun getProjectLayout(): ProjectLayout 63 | @Inject 64 | protected abstract fun getExecOperations(): ExecOperations 65 | // Used for incremental builds only 66 | @Input 67 | protected fun getProofreadingStatus(): String { 68 | val outputStream = ByteArrayOutputStream() 69 | 70 | getExecOperations().exec { 71 | it.workingDir = getProjectLayout().projectDirectory.asFile 72 | it.executable = "crowdin" 73 | it.args = mutableListOf("status", "proofreading", "--verbose", "--no-progress", "--no-colors") 74 | it.standardOutput = outputStream 75 | } 76 | 77 | return outputStream.toString() 78 | } 79 | @get:Input 80 | abstract val exportOnlyApproved: Property 81 | 82 | init { 83 | // This is the way recommended in Gradle docs 84 | @Suppress("LeakingThis") 85 | exportOnlyApproved.convention(false) 86 | } 87 | 88 | @TaskAction 89 | fun downloadTranslations() { 90 | val argList = mutableListOf("download", "--no-progress", "--no-colors") 91 | 92 | if (exportOnlyApproved.get()) { 93 | argList.add(1, "--export-only-approved") 94 | } 95 | 96 | getExecOperations().exec { 97 | it.workingDir = getProjectLayout().projectDirectory.asFile 98 | it.executable = "crowdin" 99 | it.args = argList 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /buildSrc/src/main/java/ca/tirelesstraveler/UploadTranslationsTask.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler 24 | 25 | import org.gradle.api.DefaultTask 26 | import org.gradle.api.file.ProjectLayout 27 | import org.gradle.api.tasks.TaskAction 28 | import org.gradle.process.ExecOperations 29 | import javax.inject.Inject 30 | 31 | abstract class UploadTranslationsTask: DefaultTask() { 32 | @Inject 33 | protected abstract fun getProjectLayout(): ProjectLayout 34 | @Inject 35 | protected abstract fun getExecOperations(): ExecOperations 36 | 37 | @TaskAction 38 | fun uploadTranslations() { 39 | getExecOperations().exec { 40 | it.workingDir = getProjectLayout().projectDirectory.asFile 41 | it.executable = "crowdin" 42 | it.args = mutableListOf("upload", "sources", "--no-progress", "--no-colors") 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Your Crowdin credentials 3 | # 4 | "project_id_env": "CROWDIN_PROJECT_ID" 5 | "api_token_env": "CROWDIN_PERSONAL_TOKEN" 6 | 7 | # 8 | # Choose file structure in Crowdin 9 | # e.g. true or false 10 | # 11 | "preserve_hierarchy": true 12 | 13 | # 14 | # Files configuration 15 | # 16 | files: [ 17 | { 18 | # 19 | # Source files filter 20 | # e.g. "/resources/en/*.json" 21 | # 22 | "source": "/src/main/resources/assets/fancywarpmenu/lang/en_US.lang", 23 | 24 | # 25 | # Where translations will be placed 26 | # e.g. "/resources/%two_letters_code%/%original_file_name%" 27 | # 28 | "translation": "/build/generated/resources/crowdin/assets/fancywarpmenu/lang/%locale_with_underscore%.lang", 29 | 30 | # 31 | # Files or directories for ignore 32 | # e.g. ["/**/?.txt", "/**/[0-9].txt", "/**/*\?*.txt"] 33 | # 34 | # "ignore": [], 35 | 36 | # 37 | # The dest allows you to specify a file name in Crowdin 38 | # e.g. "/messages.json" 39 | # 40 | "dest": "/%file_name%.properties", 41 | 42 | # 43 | # File type 44 | # e.g. "json" 45 | # 46 | "type": "properties", 47 | 48 | # 49 | # The parameter "update_option" is optional. If it is not set, after the files update the translations for changed strings will be removed. Use to fix typos and for minor changes in the source strings 50 | # e.g. "update_as_unapproved" or "update_without_changes" 51 | # 52 | "update_option": "update_as_unapproved", 53 | 54 | # 55 | # Start .properties block 56 | # 57 | 58 | # 59 | # Defines whether single quote should be escaped by another single quote or backslash in exported translations 60 | # e.g. 0 or 1 or 2 or 3 (Default is 3) 61 | # 0 - do not escape single quote; 62 | # 1 - escape single quote by another single quote; 63 | # 2 - escape single quote by backslash; 64 | # 3 - escape single quote by another single quote only in strings containing variables ( {0} ). 65 | # 66 | "escape_quotes": 3, 67 | 68 | # 69 | # Defines whether any special characters (=, :, ! and #) should be escaped by backslash in exported translations. 70 | # e.g. 0 or 1 (Default is 0) 71 | # 0 - do not escape special characters 72 | # 1 - escape special characters by a backslash 73 | # 74 | "escape_special_characters": 1 75 | 76 | # 77 | # End .properties block 78 | # 79 | } 80 | ] -------------------------------------------------------------------------------- /docs/Fancy Warp Menu Large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/docs/Fancy Warp Menu Large.png -------------------------------------------------------------------------------- /docs/Fancy Warp Menu Normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/docs/Fancy Warp Menu Normal.png -------------------------------------------------------------------------------- /docs/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/docs/Settings.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=forge 2 | org.gradle.jvmargs=-Xmx2g 3 | baseGroup = ca.tirelesstraveler 4 | mcVersion = 1.8.9 5 | modid = fancywarpmenu 6 | version = 2.0-beta.1 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | maven("https://oss.sonatype.org/content/repositories/snapshots") 6 | maven("https://maven.architectury.dev/") 7 | maven("https://maven.fabricmc.net") 8 | maven("https://maven.minecraftforge.net/") 9 | maven("https://repo.spongepowered.org/maven/") 10 | maven("https://repo.essential.gg/repository/maven-public") 11 | } 12 | resolutionStrategy { 13 | eachPlugin { 14 | when (requested.id.id) { 15 | "gg.essential.loom" -> useModule("gg.essential:architectury-loom:${requested.version}") 16 | } 17 | } 18 | } 19 | } 20 | 21 | rootProject.name = "FancyWarpMenu" 22 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/LogHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 26 | import org.apache.logging.log4j.LogManager; 27 | import org.apache.logging.log4j.Logger; 28 | import org.apache.logging.log4j.message.Message; 29 | 30 | public class LogHelper { 31 | public static void logDebug(String message, Throwable throwable) { 32 | logDebug(message, throwable, new Object[0]); 33 | } 34 | 35 | public static void logDebug(String message, Object... params) { 36 | logDebug(message, null, params); 37 | } 38 | 39 | /** 40 | * Workaround to allow easy toggling of debug logging in a production environment 41 | *
42 | * Add {@code -Dfancywarpmenu.debug=true} to JVM arguments to enable debug logging 43 | * 44 | * @see Logger#debug(Message) 45 | */ 46 | public static void logDebug(String message, Throwable throwable, Object... params) { 47 | String callingClassName = new Throwable().getStackTrace()[3].getClassName(); 48 | Logger logger = LogManager.getLogger(callingClassName); 49 | 50 | if (Settings.isDebugModeEnabled()) { 51 | if (throwable != null && params != null && params.length > 0) { 52 | throw new IllegalArgumentException("Throwable and params cannot be used together in the same call"); 53 | } 54 | 55 | if (throwable != null) { 56 | logger.info(message, throwable); 57 | } else { 58 | logger.info(message, params); 59 | } 60 | } else { 61 | logger.debug(message); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/commands/FancyWarpMenuCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.commands; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 26 | import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; 27 | import ca.tirelesstraveler.fancywarpmenu.utils.ChatUtils; 28 | import net.minecraft.command.*; 29 | import net.minecraft.util.ChatComponentTranslation; 30 | import net.minecraft.util.ChatStyle; 31 | import net.minecraft.util.EnumChatFormatting; 32 | 33 | import java.util.Collections; 34 | import java.util.List; 35 | 36 | /** 37 | * This is the main command of the mod.
38 | * Syntax
39 | *
    40 | *
  • /fancywarpmenu <args>
  • 41 | *
  • /fwm <args>
  • 42 | *
43 | *
44 | * Arguments 45 | *
    46 | *
  • (empty) - Open mod config menu
  • 47 | *
  • "on" or "1" - Enable Fancy Warp Menu
  • 48 | *
  • "off" or "0" - Disable Fancy Warp Menu
  • 49 | *
50 | */ 51 | public class FancyWarpMenuCommand extends CommandBase { 52 | @Override 53 | public int getRequiredPermissionLevel() { 54 | return 0; 55 | } 56 | 57 | @Override 58 | public List getCommandAliases() { 59 | return Collections.singletonList("fwm"); 60 | } 61 | 62 | @Override 63 | public String getCommandName() { 64 | return "fancywarpmenu"; 65 | } 66 | 67 | @Override 68 | public String getCommandUsage(ICommandSender sender) { 69 | // Is not shown in a server-side help menu 70 | return ""; 71 | } 72 | 73 | /** 74 | * The chat GUI closes the current screen after executing the command. 75 | * The {@link net.minecraftforge.event.CommandEvent} is intercepted instead to prevent the screen from being closed. 76 | */ 77 | @Override 78 | public void processCommand(ICommandSender sender, String[] args) throws CommandException { 79 | if (args.length > 0) { 80 | switch (args[0]) { 81 | case "1": 82 | case "on": 83 | Settings.setWarpMenuEnabled(true); 84 | ChatUtils.sendMessageWithModNamePrefix( 85 | new ChatComponentTranslation("fancywarpmenu.messages.fancyWarpMenuEnabled") 86 | .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GREEN))); 87 | break; 88 | case "0": 89 | case "off": 90 | Settings.setWarpMenuEnabled(false); 91 | ChatUtils.sendMessageWithModNamePrefix( 92 | new ChatComponentTranslation("fancywarpmenu.messages.fancyWarpMenuDisabled") 93 | .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); 94 | break; 95 | default: 96 | throw new SyntaxErrorException(); 97 | } 98 | } else { 99 | FancyWarpMenuState.setOpenConfigMenuRequested(true); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/Button.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import net.minecraft.client.gui.GuiScreen; 26 | import net.minecraft.client.gui.ScaledResolution; 27 | import net.minecraft.util.ResourceLocation; 28 | 29 | import static ca.tirelesstraveler.fancywarpmenu.resourceloaders.ResourceLoader.gson; 30 | 31 | /** 32 | * Class that holds the settings for drawing buttons that are not islands, like the config button. 33 | * This class should not be used directly. Subclasses should provide their own textures and additional fields. 34 | */ 35 | @SuppressWarnings("unused") 36 | public abstract class Button { 37 | 38 | /** x-coordinate on {@link ca.tirelesstraveler.fancywarpmenu.gui.GuiFancyWarp} to draw the button at (0-{@link Island#GRID_UNIT_WIDTH_FACTOR}) */ 39 | private int gridX; 40 | /** y-coordinate on {@link ca.tirelesstraveler.fancywarpmenu.gui.GuiFancyWarp} to draw the button at (0-{@link Island#GRID_UNIT_HEIGHT_FACTOR}) */ 41 | private int gridY; 42 | /** Width to render the button texture at as a percentage of the screen width. Texture height is set automatically. */ 43 | private float widthPercentage; 44 | /** Width of the icon texture in pixels, used to set the width of the button */ 45 | private transient int textureWidth; 46 | /** Height of the icon texture in pixels, used to set the height of the button */ 47 | private transient int textureHeight; 48 | /** Width of the button in pixels */ 49 | private transient int width; 50 | /** Height of the button in pixels */ 51 | private transient int height; 52 | 53 | Button(){} 54 | 55 | public abstract ResourceLocation getTextureLocation(); 56 | 57 | public int getGridX() { 58 | return gridX; 59 | } 60 | 61 | public int getGridY() { 62 | return gridY; 63 | } 64 | 65 | public float getWidthPercentage() { 66 | return widthPercentage; 67 | } 68 | 69 | public int getWidth() { 70 | return width; 71 | } 72 | 73 | public int getHeight() { 74 | return height; 75 | } 76 | 77 | /** 78 | * Initialize button width and height. 79 | * This should be called in {@link GuiScreen#initGui()}. 80 | */ 81 | public void init(ScaledResolution res) { 82 | float scaleFactor; 83 | width = (int) (res.getScaledWidth() * widthPercentage); 84 | scaleFactor = (float) width / textureWidth; 85 | height = (int) (textureHeight * scaleFactor); 86 | } 87 | 88 | public void setTextureDimensions(int textureWidth, int textureHeight) { 89 | this.textureWidth = textureWidth; 90 | this.textureHeight = textureHeight; 91 | } 92 | 93 | public String toString() { 94 | return gson.toJson(this); 95 | } 96 | 97 | public static void validateButton(Button button) { 98 | if (button.gridX < 0 || button.gridX > Island.GRID_UNIT_WIDTH_FACTOR) { 99 | throw new IllegalArgumentException("Button gridX must be between 0 and " + Island.GRID_UNIT_WIDTH_FACTOR + " inclusive"); 100 | } 101 | 102 | if (button.gridY < 0 || button.gridY > Island.GRID_UNIT_HEIGHT_FACTOR) { 103 | throw new IllegalArgumentException("Button gridX must be between 0 and " + Island.GRID_UNIT_HEIGHT_FACTOR + " inclusive"); 104 | } 105 | 106 | // A button width of zero causes a stack overflow 107 | if (button.widthPercentage <= 0 || button.widthPercentage > 1) { 108 | throw new IllegalArgumentException("Button icon widthPercentage must be within the interval (0,1]"); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/ConfigButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import net.minecraft.client.Minecraft; 27 | import net.minecraft.client.resources.IResource; 28 | import net.minecraft.util.ResourceLocation; 29 | import org.apache.commons.io.IOUtils; 30 | 31 | import java.io.IOException; 32 | 33 | /** 34 | * Class that holds the settings for drawing the config button that opens the mod's settings 35 | */ 36 | @SuppressWarnings("unused") 37 | public class ConfigButton extends Button { 38 | public static final ResourceLocation TEXTURE_LOCATION = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), "textures/gui/Logo.png"); 39 | /** Overlay texture rendered when mod is outdated */ 40 | public static final ResourceLocation NOTIFICATION_TEXTURE_LOCATION = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), "textures/gui/Notification.png"); 41 | 42 | private ConfigButton(){} 43 | 44 | public ResourceLocation getTextureLocation() { 45 | return TEXTURE_LOCATION; 46 | } 47 | 48 | public static void validateConfigButtonIcon(ConfigButton configButton) throws IllegalArgumentException, NullPointerException { 49 | if (configButton == null) { 50 | throw new NullPointerException("Config button settings cannot be null"); 51 | } 52 | 53 | Button.validateButton(configButton); 54 | 55 | try { 56 | IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(ConfigButton.TEXTURE_LOCATION); 57 | IOUtils.closeQuietly(resource.getInputStream()); 58 | } catch (IOException e) { 59 | throw new RuntimeException(String.format("Config button texture not found at %s", ConfigButton.TEXTURE_LOCATION)); 60 | } 61 | 62 | try { 63 | IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(ConfigButton.NOTIFICATION_TEXTURE_LOCATION); 64 | IOUtils.closeQuietly(resource.getInputStream()); 65 | } catch (IOException e) { 66 | throw new RuntimeException(String.format("Config button notification texture not found at %s", ConfigButton.NOTIFICATION_TEXTURE_LOCATION)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/Layout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import net.minecraft.client.Minecraft; 27 | import net.minecraft.client.resources.IResource; 28 | import net.minecraft.util.ResourceLocation; 29 | import org.apache.commons.io.IOUtils; 30 | 31 | import java.io.IOException; 32 | import java.util.List; 33 | 34 | import static ca.tirelesstraveler.fancywarpmenu.resourceloaders.ResourceLoader.gson; 35 | 36 | @SuppressWarnings("unused") 37 | public class Layout { 38 | 39 | /** Path to the texture to be shown as the background of the fancy warp menu */ 40 | private String backgroundTexturePath; 41 | /** The islands to be shown in the fancy warp menu */ 42 | private List islandList; 43 | /** Texture settings for the warp icon, which is drawn for all warp buttons */ 44 | private WarpIcon warpIcon; 45 | /** Position and size settings for the button that opens the mod's settings menu */ 46 | private ConfigButton configButton; 47 | /** Position and size settings for the button that turns off the fancy warp menu */ 48 | private RegularWarpMenuButton regularWarpMenuButton; 49 | 50 | private transient ResourceLocation backgroundTextureLocation; 51 | 52 | private Layout(){} 53 | 54 | public List getIslandList() { 55 | return islandList; 56 | } 57 | 58 | public WarpIcon getWarpIcon() { 59 | return warpIcon; 60 | } 61 | 62 | public ConfigButton getConfigButton() { 63 | return configButton; 64 | } 65 | 66 | public RegularWarpMenuButton getRegularWarpMenuButton() { 67 | return regularWarpMenuButton; 68 | } 69 | 70 | public ResourceLocation getBackgroundTextureLocation() { 71 | return backgroundTextureLocation; 72 | } 73 | 74 | public void setBackgroundTextureLocation() { 75 | if (backgroundTexturePath != null) { 76 | backgroundTextureLocation = 77 | new ResourceLocation(FancyWarpMenu.getInstance().getModId(), backgroundTexturePath); 78 | } 79 | } 80 | 81 | public static void validateLayout(Layout layout) throws IllegalArgumentException, NullPointerException { 82 | if (layout == null) { 83 | throw new NullPointerException("Layout cannot be null"); 84 | } 85 | 86 | if (layout.backgroundTexturePath != null) { 87 | ResourceLocation textureLocation = 88 | new ResourceLocation(FancyWarpMenu.getInstance().getModId(), layout.backgroundTexturePath); 89 | try { 90 | IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(textureLocation); 91 | IOUtils.closeQuietly(resource.getInputStream()); 92 | } catch (IOException e) { 93 | throw new RuntimeException(String.format("Layout background texture path not found at %s", textureLocation)); 94 | } 95 | } 96 | 97 | if (layout.islandList == null || layout.islandList.isEmpty()) { 98 | throw new IllegalArgumentException("Island list cannot be empty"); 99 | } 100 | 101 | for (Island island : layout.getIslandList()) { 102 | Island.validateIsland(island); 103 | } 104 | 105 | WarpIcon.validateWarpIcon(layout.getWarpIcon()); 106 | ConfigButton.validateConfigButtonIcon(layout.getConfigButton()); 107 | RegularWarpMenuButton.validateRegularMenuButtonIcon(layout.getRegularWarpMenuButton()); 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | return gson.toJson(this); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/RegularWarpMenuButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import net.minecraft.client.Minecraft; 27 | import net.minecraft.client.resources.IResource; 28 | import net.minecraft.util.ResourceLocation; 29 | import org.apache.commons.io.IOUtils; 30 | 31 | import java.io.IOException; 32 | 33 | /** 34 | * Class that holds the settings for drawing the button that opens the regular warp menu 35 | */ 36 | @SuppressWarnings("unused") 37 | public class RegularWarpMenuButton extends Button { 38 | public static final ResourceLocation TEXTURE_LOCATION = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), "textures/gui/Regular Warp Menu.png"); 39 | 40 | private RegularWarpMenuButton(){} 41 | 42 | public ResourceLocation getTextureLocation() { 43 | return TEXTURE_LOCATION; 44 | } 45 | 46 | public static void validateRegularMenuButtonIcon(RegularWarpMenuButton regularWarpMenuButton) throws IllegalArgumentException, NullPointerException { 47 | if (regularWarpMenuButton == null) { 48 | throw new NullPointerException("Regular warp menu button settings cannot be null"); 49 | } 50 | 51 | Button.validateButton(regularWarpMenuButton); 52 | 53 | try { 54 | IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(RegularWarpMenuButton.TEXTURE_LOCATION); 55 | IOUtils.closeQuietly(resource.getInputStream()); 56 | } catch (IOException e) { 57 | throw new RuntimeException(String.format("Config button texture not found at %s", RegularWarpMenuButton.TEXTURE_LOCATION)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/Warp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import net.minecraft.client.gui.GuiScreen; 26 | import net.minecraft.client.gui.ScaledResolution; 27 | import net.minecraft.util.ResourceLocation; 28 | import net.minecraft.util.StringUtils; 29 | 30 | import java.util.List; 31 | import java.util.regex.Pattern; 32 | 33 | import static ca.tirelesstraveler.fancywarpmenu.resourceloaders.ResourceLoader.gson; 34 | 35 | /** 36 | * Warp entry data used to create the warp buttons on the GUI 37 | */ 38 | @SuppressWarnings({"unused", "FieldMayBeFinal"}) 39 | public class Warp { 40 | // Height scale is the same as width 41 | /** Grid unit width is islandWidth / widthFactor */ 42 | public static final int GRID_UNIT_WIDTH_FACTOR = 40; 43 | /** Pattern used to validate tags in {@link Warp#validateWarp(Warp)} */ 44 | private static final Pattern tagValidationPattern = Pattern.compile("[a-z\\d-]"); 45 | /** Warp button texture, shared between all warp buttons */ 46 | public static WarpIcon warpIcon; 47 | /** Warp button width in pixels, see {@link this#initDefaults(ScaledResolution)} */ 48 | private static int width; 49 | /** Warp button height in pixels, see {@link this#initDefaults(ScaledResolution)} */ 50 | private static int height; 51 | /** x-coordinate to draw the warp button at (0-40) */ 52 | private int gridX; 53 | /** y-coordinate to draw the warp button at (0-40) */ 54 | private int gridY; 55 | /** Name of the warp, rendered below the warp button texture */ 56 | private String displayName; 57 | /** Name of the warp as used in the {@code /warp} command */ 58 | private String commandName; 59 | /** Tags used to group related warps together */ 60 | private List tags; 61 | /** Index of the inventory slot corresponding to this warp in a warp {@code GuiChest} */ 62 | private int slotIndex; 63 | /** Skips drawing the warp button, useful for islands with only one warp */ 64 | private boolean hideButton; 65 | 66 | private Warp() { 67 | slotIndex = -1; 68 | } 69 | 70 | public String getDisplayName() { 71 | return displayName; 72 | } 73 | 74 | public ResourceLocation getWarpTextureLocation() { 75 | return warpIcon.getTextureLocation(); 76 | } 77 | 78 | public ResourceLocation getWarpHoverEffectTextureLocation() { 79 | return warpIcon.getHoverEffectTextureLocation(); 80 | } 81 | 82 | /** 83 | * Returns the command the player has to send to use this warp. 84 | * If the {@code commandName} doesn't start with a '/', "/warp " is prepended. 85 | */ 86 | public String getWarpCommand() { 87 | // hardcoded to prevent command injection 88 | return commandName.equals("/garry") ? commandName : "/warp " + commandName; 89 | } 90 | 91 | /** 92 | * Returns the list of category tags 93 | */ 94 | public List getTags() { 95 | return tags; 96 | } 97 | 98 | public int getSlotIndex() { 99 | return slotIndex; 100 | } 101 | 102 | public int getWidth() { 103 | return width; 104 | } 105 | 106 | public int getHeight() { 107 | return height; 108 | } 109 | 110 | public int getGridX() { 111 | return gridX; 112 | } 113 | 114 | public int getGridY() { 115 | return gridY; 116 | } 117 | 118 | public boolean shouldHideButton() { 119 | return hideButton; 120 | } 121 | 122 | public String toString() { 123 | return gson.toJson(this); 124 | } 125 | 126 | /** 127 | * Initializes width and height for all warp buttons. 128 | * This should be called in {@link GuiScreen#initGui()}. 129 | */ 130 | public static void initDefaults(ScaledResolution res) { 131 | float scaleFactor; 132 | width = (int) (res.getScaledWidth() * warpIcon.getWidthPercentage()); 133 | scaleFactor = (float) width / warpIcon.getTextureWidth(); 134 | height = (int) (warpIcon.getTextureHeight() * scaleFactor); 135 | } 136 | 137 | public static void setWarpIcon(WarpIcon warpIcon) { 138 | Warp.warpIcon = warpIcon; 139 | } 140 | 141 | public static void validateWarp(Warp warp) throws IllegalArgumentException, NullPointerException { 142 | if (warp == null) { 143 | throw new NullPointerException("Warp cannot be null"); 144 | } 145 | 146 | String name = warp.displayName; 147 | if (name == null) { 148 | throw new IllegalArgumentException(String.format("The following warp lacks a name: %s", warp)); 149 | } 150 | 151 | if (StringUtils.isNullOrEmpty(warp.commandName) && warp.slotIndex < 0) { 152 | throw new IllegalArgumentException(String.format("Warp %s must have a command name or a slot index", warp.displayName)); 153 | } 154 | 155 | if (warp.commandName != null && !warp.commandName.matches("(?i)/?[a-z]+")) { 156 | throw new IllegalArgumentException("Warp %s's command name contains invalid characters."); 157 | } 158 | 159 | if (warp.tags != null) { 160 | for (String tag : warp.tags) { 161 | if (!tagValidationPattern.asPredicate().test(tag)) { 162 | throw new IllegalArgumentException(String.format("\"%s\" is not a valid warp tag.", tag)); 163 | } 164 | } 165 | } 166 | 167 | if (warp.gridX < 0 || warp.gridX > GRID_UNIT_WIDTH_FACTOR) { 168 | throw new IllegalArgumentException(String.format("Warp %s gridX is outside island", name)); 169 | } 170 | 171 | if (warp.gridY < 0 || warp.gridY > GRID_UNIT_WIDTH_FACTOR) { 172 | throw new IllegalArgumentException(String.format("Warp %s gridY is outside island", name)); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/layout/WarpIcon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.layout; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import net.minecraft.client.Minecraft; 27 | import net.minecraft.client.resources.IResource; 28 | import net.minecraft.util.ResourceLocation; 29 | import org.apache.commons.io.IOUtils; 30 | 31 | import java.io.IOException; 32 | 33 | import static ca.tirelesstraveler.fancywarpmenu.resourceloaders.ResourceLoader.gson; 34 | 35 | /** 36 | * Class that holds the settings for drawing the warp icon (portal) 37 | */ 38 | @SuppressWarnings("unused") 39 | public class WarpIcon { 40 | /** Path to the warp button texture relative to {@code resources/assets/fancywarpmenu} */ 41 | private String texturePath; 42 | /** Path to the texture to be drawn on top of the button texture when the button is hovered, relative to {@code resources/assets/fancywarpmenu} */ 43 | private String hoverEffectTexturePath; 44 | 45 | /** Width to render the warp icon texture at as a percentage of the screen width. Texture height is set automatically. */ 46 | private float widthPercentage; 47 | private transient ResourceLocation textureLocation; 48 | private transient ResourceLocation hoverEffectTextureLocation; 49 | /** Width of the warp icon texture, used to set the hit box of the warp button */ 50 | private transient int textureWidth; 51 | /** Height of the warp icon texture, used to set the hit box of the warp button */ 52 | private transient int textureHeight; 53 | 54 | private WarpIcon(){} 55 | 56 | public void init() { 57 | textureLocation = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), texturePath); 58 | 59 | if (hoverEffectTexturePath != null) { 60 | hoverEffectTextureLocation = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), hoverEffectTexturePath); 61 | } 62 | } 63 | 64 | public ResourceLocation getTextureLocation() { 65 | return textureLocation; 66 | } 67 | 68 | public ResourceLocation getHoverEffectTextureLocation() { 69 | return hoverEffectTextureLocation; 70 | } 71 | 72 | public float getWidthPercentage() { 73 | return widthPercentage; 74 | } 75 | 76 | public int getTextureWidth() { 77 | return textureWidth; 78 | } 79 | 80 | public int getTextureHeight() { 81 | return textureHeight; 82 | } 83 | 84 | public void setTextureDimensions(int textureWidth, int textureHeight) { 85 | this.textureWidth = textureWidth; 86 | this.textureHeight = textureHeight; 87 | } 88 | public String toString() { 89 | return gson.toJson(this); 90 | } 91 | 92 | public static void validateWarpIcon(WarpIcon warpIcon) throws IllegalArgumentException, NullPointerException { 93 | if (warpIcon == null) { 94 | throw new NullPointerException("Warp icon cannot be null"); 95 | } 96 | 97 | if (warpIcon.texturePath == null) { 98 | throw new NullPointerException("Warp icon texture path cannot be null"); 99 | } 100 | 101 | ResourceLocation textureLocation = new ResourceLocation(FancyWarpMenu.getInstance().getModId(), warpIcon.texturePath); 102 | try { 103 | IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(textureLocation); 104 | IOUtils.closeQuietly(resource.getInputStream()); 105 | } catch (IOException e) { 106 | throw new RuntimeException(String.format("Warp icon texture not found at %s", textureLocation)); 107 | } 108 | 109 | if (warpIcon.widthPercentage < 0 || warpIcon.widthPercentage > 1) { 110 | throw new IllegalArgumentException("Warp icon widthPercentage must be between 0 and 1"); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/SkyBlockConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; 26 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 27 | import org.jetbrains.annotations.NotNull; 28 | 29 | import java.util.List; 30 | import java.util.Map; 31 | 32 | import static ca.tirelesstraveler.fancywarpmenu.resourceloaders.ResourceLoader.gson; 33 | 34 | @SuppressWarnings("unused") 35 | public class SkyBlockConstants { 36 | public static final String WARP_COMMAND_BASE = "/warp"; 37 | 38 | /** Map of match conditions used to identify SkyBlock menus */ 39 | private Map> menuMatchingMap; 40 | 41 | /** Chat messages sent by the server when a warp attempt succeeds or fails */ 42 | private WarpMessages warpMessages; 43 | /** Names of the warp command and its aliases */ 44 | private List warpCommandVariants; 45 | /** Chat messages are checked to see if they start with this string in order to see if the player joined SkyBlock */ 46 | private String skyBlockJoinMessage; 47 | 48 | private SkyBlockConstants() { 49 | } 50 | 51 | public Map> getMenuMatchingMap() { 52 | return menuMatchingMap; 53 | } 54 | 55 | /** 56 | * Returns the inventory slot index of the last {@link ItemMatchCondition} for the given {@link Menu}. 57 | * 58 | * @param menu the {@code Menu} to get the inventory slot index from 59 | * @return the inventory slot index of the last {@code ItemMatchCondition} for the given {@code Menu} 60 | */ 61 | public int getLastMatchConditionInventorySlotIndex(Menu menu) { 62 | List matchConditions = menuMatchingMap.get(menu); 63 | 64 | return matchConditions.get(matchConditions.size() - 1).getInventorySlotIndex(); 65 | } 66 | 67 | public WarpMessages getWarpMessages() { 68 | return warpMessages; 69 | } 70 | 71 | public List getWarpCommandVariants() { 72 | return warpCommandVariants; 73 | } 74 | 75 | public String getSkyBlockJoinMessage() { 76 | return skyBlockJoinMessage; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return gson.toJson(this); 82 | } 83 | 84 | public static void validateSkyBlockConstants(SkyBlockConstants skyBlockConstants) { 85 | if (skyBlockConstants == null) { 86 | throw new NullPointerException("SkyBlock constants cannot be null"); 87 | } 88 | 89 | for(Map.Entry> menuMatchingMapEntry : skyBlockConstants.menuMatchingMap.entrySet()) { 90 | List matchConditions = getMenuMatchConditions(menuMatchingMapEntry); 91 | 92 | for (ItemMatchCondition matchCondition : matchConditions) { 93 | matchCondition.validateCondition(); 94 | } 95 | } 96 | 97 | WarpMessages.validateWarpMessages(skyBlockConstants.getWarpMessages()); 98 | 99 | if (skyBlockConstants.warpCommandVariants == null || skyBlockConstants.warpCommandVariants.isEmpty()) { 100 | throw new NullPointerException("Warp command variant list cannot be empty"); 101 | } 102 | 103 | for (WarpCommandVariant warpCommandVariant : skyBlockConstants.warpCommandVariants) { 104 | WarpCommandVariant.validateWarpCommandVariant(warpCommandVariant); 105 | } 106 | } 107 | 108 | @NotNull 109 | private static List getMenuMatchConditions(Map.Entry> menuMatchingMapEntry) { 110 | List matchConditions = menuMatchingMapEntry.getValue(); 111 | 112 | if (matchConditions == null) { 113 | throw new NullPointerException(String.format("Menu %s's menu match conditions list cannot be null", 114 | menuMatchingMapEntry.getKey().name())); 115 | } else if (matchConditions.isEmpty()) { 116 | throw new IllegalArgumentException(String.format("Menu %s's menu match conditions list cannot be empty", 117 | menuMatchingMapEntry.getKey().name())); 118 | } 119 | return matchConditions; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/WarpCommandVariant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants; 24 | 25 | @SuppressWarnings("unused") 26 | public class WarpCommandVariant { 27 | private String command; 28 | private WarpCommandType type; 29 | 30 | private WarpCommandVariant(){ 31 | } 32 | 33 | public String getCommand() { 34 | return command; 35 | } 36 | 37 | public WarpCommandType getType() { 38 | return type; 39 | } 40 | 41 | public enum WarpCommandType { 42 | /** 43 | * An alias that works like the actual /warp command 44 | */ 45 | ALIAS, 46 | /** 47 | * A shortcut to teleport to a single warp 48 | */ 49 | WARP 50 | } 51 | 52 | public static void validateWarpCommandVariant(WarpCommandVariant warpCommandVariant) { 53 | if (warpCommandVariant == null) { 54 | throw new NullPointerException("Warp command variant cannot be null"); 55 | } 56 | 57 | if (warpCommandVariant.command == null) { 58 | throw new NullPointerException("Warp command variant's command cannot be null"); 59 | } 60 | 61 | if (warpCommandVariant.type == null) { 62 | throw new NullPointerException("Warp command variant's command type cannot be null"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/WarpMessages.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants; 24 | 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | @SuppressWarnings("unused") 29 | public class WarpMessages { 30 | private List warpSuccessMessages; 31 | /** key: chat message, value: translation key of message to show in warp menu */ 32 | private Map warpFailMessages; 33 | 34 | private WarpMessages() { 35 | } 36 | 37 | public List getWarpSuccessMessages() { 38 | return warpSuccessMessages; 39 | } 40 | 41 | public Map getWarpFailMessages() { 42 | return warpFailMessages; 43 | } 44 | 45 | public static void validateWarpMessages(WarpMessages warpMessages) throws IllegalArgumentException, NullPointerException { 46 | if (warpMessages == null) { 47 | throw new NullPointerException("Warp messages cannot be null"); 48 | } 49 | 50 | List successMessages = warpMessages.getWarpSuccessMessages(); 51 | if (successMessages == null || successMessages.isEmpty()) { 52 | throw new IllegalArgumentException("Warp success message list cannot be empty"); 53 | } 54 | 55 | Map failMessages = warpMessages.getWarpFailMessages(); 56 | if (failMessages == null || failMessages.isEmpty()) { 57 | throw new IllegalArgumentException("Warp fail message list cannot be empty"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/data/skyblockconstants/menu/Menu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu; 24 | 25 | /** 26 | * In-game menus, not serialized 27 | */ 28 | public enum Menu { 29 | /** Value used when player is not in a menu or in an unknown or irrelevant menu */ 30 | NONE(""), 31 | SKYBLOCK_MENU("SkyBlock Menu"), 32 | FAST_TRAVEL("Fast Travel"), 33 | PORHTAL("Porhtal"); 34 | 35 | /** 36 | * Menu name as displayed at the top of the {@code GuiChest} 37 | */ 38 | final String MENU_DISPLAY_NAME; 39 | 40 | Menu(String menuDisplayName) { 41 | this.MENU_DISPLAY_NAME = menuDisplayName; 42 | } 43 | 44 | public String getMenuDisplayName() { 45 | return MENU_DISPLAY_NAME; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/FancyWarpMenuConfigScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 26 | import net.minecraft.client.gui.GuiScreen; 27 | import net.minecraft.client.resources.I18n; 28 | import net.minecraft.event.ClickEvent; 29 | import net.minecraft.util.ChatComponentText; 30 | import net.minecraft.util.ChatStyle; 31 | import net.minecraft.util.EnumChatFormatting; 32 | import net.minecraft.util.IChatComponent; 33 | import net.minecraftforge.fml.client.config.GuiConfig; 34 | import net.minecraftforge.fml.client.config.GuiConfigEntries; 35 | import net.minecraftforge.fml.client.config.IConfigElement; 36 | 37 | import java.io.IOException; 38 | import java.net.URI; 39 | import java.net.URISyntaxException; 40 | import java.net.URL; 41 | 42 | public class FancyWarpMenuConfigScreen extends GuiConfig { 43 | public FancyWarpMenuConfigScreen(GuiScreen parent) 44 | { 45 | super(parent, Settings.getConfigElements(), "fancywarpmenu", "main", false, false, I18n.format("fancywarpmenu.config.title"), I18n.format("fancywarpmenu.config.subtitle")); 46 | } 47 | 48 | void openLink(URI link) { 49 | IChatComponent chatComponent = new ChatComponentText(null).setChatStyle(new ChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, link.toString()))); 50 | handleComponentClick(chatComponent); 51 | } 52 | 53 | /** 54 | * A {@link GuiConfigEntries.CategoryEntry} implementation that changes the color of the label to a supplied chat color 55 | *

Example

56 | *

DummyConfigElement updateAvailableCategory = new DummyConfigElement.DummyCategoryElement(CATEGORY_UPDATE_AVAILABLE, "fancywarpmenu.config.categories.updateAvailable", updateAvailableElements);

57 | *

updateAvailableCategory.set(EnumChatFormatting.GREEN);

58 | *

updateAvailableCategory.setConfigEntryClass(FancyWarpMenuConfigScreen.ColoredCategoryEntry.class);

59 | */ 60 | public static class ColoredCategoryEntry extends GuiConfigEntries.CategoryEntry 61 | { 62 | public ColoredCategoryEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { 63 | super(owningScreen, owningEntryList, prop); 64 | 65 | Object colorCodeObject = configElement.getDefault(); 66 | 67 | if (colorCodeObject != null) { 68 | if (colorCodeObject instanceof EnumChatFormatting) { 69 | String colorCode = colorCodeObject.toString(); 70 | btnSelectCategory.displayString = colorCode + btnSelectCategory.displayString + EnumChatFormatting.RESET; 71 | } 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * A {@link GuiConfigEntries.CategoryEntry} implementation that opens a link when clicked. 78 | *

Example

79 | *

DummyConfigElement downloadUpdateElement = new DummyConfigElement("downloadUpdate", EnvironmentDetails.SUPPORT_LINK, ConfigGuiType.STRING, "fancywarpmenu.config.downloadUpdate");

80 | *

downloadUpdateElement.setConfigEntryClass(FancyWarpMenuConfigScreen.OpenLinkEntry.class);

81 | */ 82 | public static class OpenLinkEntry extends GuiConfigEntries.CategoryEntry 83 | { 84 | private URI link; 85 | 86 | public OpenLinkEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { 87 | super(owningScreen, owningEntryList, prop); 88 | 89 | // Subclasses may use the array to add additional options in addition to the link. 90 | if (getConfigElement().getDefaults() != null) { 91 | setLink(getConfigElement().getDefaults()[0]); 92 | } else { 93 | setLink(getConfigElement().getDefault()); 94 | } 95 | } 96 | 97 | @Override 98 | protected GuiScreen buildChildScreen() throws RuntimeException { 99 | return null; 100 | } 101 | 102 | @Override 103 | public boolean mousePressed(int slotIndex, int x, int y, int mouseEvent, int relativeX, int relativeY) { 104 | if (btnSelectCategory.mousePressed(this.mc, x, y)) { 105 | btnSelectCategory.playPressSound(mc.getSoundHandler()); 106 | 107 | if (owningScreen instanceof FancyWarpMenuConfigScreen) { 108 | ((FancyWarpMenuConfigScreen) owningScreen).openLink(link); 109 | } 110 | 111 | return true; 112 | } else { 113 | return false; 114 | } 115 | } 116 | 117 | protected void setLink(Object linkObject) { 118 | if (linkObject instanceof String) { 119 | try { 120 | link = new URL((String) linkObject).toURI(); 121 | } catch (URISyntaxException | IOException e) { 122 | throw new RuntimeException(linkObject + " is not a valid URL", e); 123 | } 124 | } else { 125 | throw new RuntimeException(linkObject + " is not a string"); 126 | } 127 | } 128 | } 129 | 130 | /** 131 | * A {@link GuiConfigEntries.CategoryEntry} implementation that has a colored label and opens a link when clicked. 132 | */ 133 | public static class ColoredOpenLinkEntry extends OpenLinkEntry 134 | { 135 | /** 136 | * Creates a new instance of {@code ColoredOpenLinkEntry}. 137 | * Set the value of prop to an {@code Object} array with the link at index 0 and an instance of {@code EnumChatFormatting} at index 1 for the color. 138 | * 139 | * @param owningScreen parent screen 140 | * @param owningEntryList parent config entry list 141 | * @param prop property containing the link to open and the color for the label 142 | */ 143 | public ColoredOpenLinkEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) { 144 | super(owningScreen, owningEntryList, prop); 145 | setColor(getConfigElement().getDefaults()[1]); 146 | } 147 | 148 | private void setColor(Object colorCodeObject) { 149 | if (colorCodeObject != null) { 150 | if (colorCodeObject instanceof EnumChatFormatting) { 151 | String colorCode = colorCodeObject.toString(); 152 | btnSelectCategory.displayString = colorCode + btnSelectCategory.displayString + EnumChatFormatting.RESET; 153 | } else { 154 | throw new RuntimeException(colorCodeObject + " is not an instance of EnumChatFormatting"); 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/FancyWarpMenuGuiFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui; 24 | 25 | import net.minecraft.client.Minecraft; 26 | import net.minecraft.client.gui.GuiScreen; 27 | import net.minecraftforge.fml.client.IModGuiFactory; 28 | 29 | import java.util.Set; 30 | 31 | @SuppressWarnings("unused") 32 | public class FancyWarpMenuGuiFactory implements IModGuiFactory { 33 | @Override 34 | public void initialize(Minecraft minecraft) { 35 | 36 | } 37 | 38 | @Override 39 | public Class mainConfigGuiClass() { 40 | return FancyWarpMenuConfigScreen.class; 41 | } 42 | 43 | @Override 44 | public Set runtimeGuiCategories() { 45 | return null; 46 | } 47 | 48 | @Override 49 | public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement runtimeOptionCategoryElement) { 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiFastTravel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; 28 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonIsland; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonWarp; 31 | import ca.tirelesstraveler.fancywarpmenu.utils.GameChecks; 32 | import net.minecraft.client.Minecraft; 33 | import net.minecraft.client.gui.GuiButton; 34 | import net.minecraft.inventory.IInventory; 35 | 36 | public class GuiFastTravel extends GuiFancyWarp { 37 | 38 | public GuiFastTravel(IInventory playerInventory, IInventory chestInventory, Layout layout) { 39 | super(playerInventory, chestInventory, layout); 40 | menu = Menu.FAST_TRAVEL; 41 | lastSlotIndexToCheck = FancyWarpMenu.getSkyBlockConstants().getLastMatchConditionInventorySlotIndex(menu); 42 | } 43 | 44 | @Override 45 | protected void actionPerformed(GuiButton button) { 46 | super.actionPerformed(button); 47 | 48 | // Block repeat clicks if the last warp failed 49 | if (Minecraft.getSystemTime() > warpFailCoolDownExpiryTime) { 50 | if (button instanceof GuiButtonWarp) { 51 | GuiButtonWarp warpButton = (GuiButtonWarp) button; 52 | 53 | // Don't send command twice for single warp islands 54 | if (warpButton.getIsland().getWarpCount() > 1) { 55 | String warpCommand = warpButton.getWarpCommand(); 56 | mc.thePlayer.sendChatMessage(warpCommand); 57 | } 58 | } else if (button instanceof GuiButtonIsland) { 59 | Island island = ((GuiButtonIsland) button).getIsland(); 60 | 61 | if (island.getWarpCount() == 1) { 62 | String warpCommand = island.getWarps().get(0).getWarpCommand(); 63 | mc.thePlayer.sendChatMessage(warpCommand); 64 | } 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | protected void updateButtonStates() { 71 | // Season is needed to show/hide the Jerry island button. 72 | GameChecks.checkSeason(); 73 | 74 | super.updateButtonStates(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/GuiRiftFastTravel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; 28 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonIsland; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonWarp; 31 | import net.minecraft.client.Minecraft; 32 | import net.minecraft.client.gui.GuiButton; 33 | import net.minecraft.inventory.IInventory; 34 | 35 | public class GuiRiftFastTravel extends GuiFancyWarp { 36 | 37 | public GuiRiftFastTravel(IInventory playerInventory, IInventory chestInventory, Layout layout) { 38 | super(playerInventory, chestInventory, layout); 39 | menu = Menu.PORHTAL; 40 | lastSlotIndexToCheck = FancyWarpMenu.getSkyBlockConstants().getLastMatchConditionInventorySlotIndex(menu); 41 | } 42 | 43 | @Override 44 | protected void actionPerformed(GuiButton button) { 45 | super.actionPerformed(button); 46 | 47 | // Block repeat clicks if the last warp failed 48 | if (Minecraft.getSystemTime() > warpFailCoolDownExpiryTime) { 49 | if (button instanceof GuiButtonWarp) { 50 | GuiButtonWarp warpButton = (GuiButtonWarp) button; 51 | 52 | // Don't click twice for islands with only one warp 53 | if (warpButton.getIsland().getWarpCount() > 1) { 54 | clickSlot(warpButton.getWarpSlotIndex()); 55 | } 56 | } else if (button instanceof GuiButtonIsland) { 57 | Island island = ((GuiButtonIsland) button).getIsland(); 58 | 59 | if (island.getWarpCount() == 1) { 60 | clickSlot(island.getWarps().get(0).getSlotIndex()); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonChestMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import net.minecraft.client.Minecraft; 26 | import net.minecraft.client.gui.FontRenderer; 27 | import net.minecraft.client.gui.GuiButton; 28 | import net.minecraft.util.ResourceLocation; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | import java.awt.*; 32 | 33 | /** 34 | * Button class used with {@link ca.tirelesstraveler.fancywarpmenu.gui.GuiChestMenu} 35 | */ 36 | public abstract class GuiButtonChestMenu extends GuiButton implements Comparable { 37 | protected ResourceLocation backgroundTextureLocation; 38 | protected ResourceLocation foregroundTextureLocation; 39 | 40 | /** 41 | * Constructor without coordinates for when placement is set at runtime 42 | */ 43 | public GuiButtonChestMenu(int buttonId, String buttonText) { 44 | this(buttonId, 0, 0, buttonText); 45 | } 46 | public GuiButtonChestMenu(int buttonId, int x, int y, String buttonText) { 47 | super(buttonId, x, y, buttonText); 48 | } 49 | 50 | public void setHovered(boolean hovered) { 51 | this.hovered = hovered; 52 | } 53 | 54 | /** 55 | * Calculates whether this button is hovered instead of drawing a vanilla button 56 | */ 57 | @Override 58 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 59 | if (visible) { 60 | hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height; 61 | } 62 | } 63 | 64 | /** 65 | * Draw the button's display string (label). 66 | * 67 | * @param x x-coordinate to center the string on 68 | * @param y y-coordinate to center the string on 69 | * @param rgb a colour in the integer rgb format produced by {@link Color#getRGB()} 70 | */ 71 | public void drawDisplayString(float x, float y, int rgb) { 72 | FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; 73 | String[] lines = displayString.split("\n", 3); 74 | 75 | for (int i = 0; i < lines.length; i++) { 76 | fontRenderer.drawStringWithShadow(displayString, x - (float) fontRenderer.getStringWidth(displayString) / 2, y + (10 * i), rgb); 77 | } 78 | } 79 | 80 | /** 81 | * Compares the z-level of this {@code GuiButtonChestMenu} with another {@code GuiButtonChestMenu} 82 | * Returns negative if this button's z-level is smaller than the other button's, 0 if their z-levels are equal, 83 | * and positive if this button's z-level is greater than the other button's. 84 | * 85 | * @param o the object to be compared. 86 | */ 87 | @Override 88 | public int compareTo(@NotNull GuiButtonChestMenu o) { 89 | return (int) (this.zLevel - o.zLevel); 90 | } 91 | 92 | public int getZLevel() { 93 | return (int) zLevel; 94 | } 95 | 96 | public void setEnabled(boolean enabled) { 97 | this.enabled = enabled; 98 | } 99 | 100 | public void setVisible(boolean visible) { 101 | this.visible = visible; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.ConfigButton; 28 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 29 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.GridRectangle; 31 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.ScaledGrid; 32 | import ca.tirelesstraveler.fancywarpmenu.gui.transitions.ScaleTransition; 33 | import net.minecraft.client.Minecraft; 34 | import net.minecraft.client.gui.ScaledResolution; 35 | import net.minecraft.client.resources.I18n; 36 | import net.minecraft.util.EnumChatFormatting; 37 | import net.minecraftforge.common.ForgeVersion; 38 | 39 | @SuppressWarnings("FieldCanBeLocal") 40 | public class GuiButtonConfig extends GuiButtonScaleTransition { 41 | private static final float HOVERED_SCALE = 1.2F; 42 | private static final long SCALE_TRANSITION_DURATION = 500; 43 | 44 | /** This button uses its own grid instead of the grid of the GuiScreen it belongs to since it's also attached to vanilla screens, which don't have grids */ 45 | private final ScaledGrid scaledGrid; 46 | // Far right edge 47 | private final int GRID_X; 48 | // Bottom edge 49 | private final int GRID_Y; 50 | 51 | public GuiButtonConfig(Layout layout, int buttonId, ScaledResolution res) { 52 | super(buttonId, EnumChatFormatting.GREEN + I18n.format(FancyWarpMenu.getFullLanguageKey("gui.buttons.config"))); 53 | scaledGrid = new ScaledGrid(0, 0, res.getScaledWidth(), res.getScaledHeight(), Island.GRID_UNIT_HEIGHT_FACTOR, Island.GRID_UNIT_WIDTH_FACTOR, false); 54 | ConfigButton configButtonSettings = layout.getConfigButton(); 55 | configButtonSettings.init(res); 56 | GRID_X = configButtonSettings.getGridX(); 57 | GRID_Y = configButtonSettings.getGridY(); 58 | width = configButtonSettings.getWidth(); 59 | height = configButtonSettings.getHeight(); 60 | // Above islands and warps 61 | zLevel = 20; 62 | buttonRectangle = new GridRectangle(scaledGrid, GRID_X, GRID_Y, width, height, false, true); 63 | scaledGrid.addRectangle("configButton", buttonRectangle); 64 | backgroundTextureLocation = configButtonSettings.getTextureLocation(); 65 | transition = new ScaleTransition(0, 1, 1); 66 | } 67 | 68 | @Override 69 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 70 | if (this.visible) { 71 | calculateHoverState(mouseX, mouseY); 72 | transitionStep(SCALE_TRANSITION_DURATION, HOVERED_SCALE); 73 | 74 | super.drawButton(mc, mouseX, mouseY); 75 | 76 | if (Settings.isUpdateNotificationEnabled() && FancyWarpMenu.getUpdateCheckResult() != null && FancyWarpMenu.getUpdateCheckResult().status == ForgeVersion.Status.OUTDATED) { 77 | drawButtonForegroundLayer(ConfigButton.NOTIFICATION_TEXTURE_LOCATION); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonIsland.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Warp; 28 | import ca.tirelesstraveler.fancywarpmenu.gui.GuiFancyWarp; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.ScaledGrid; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.transitions.ScaleTransition; 31 | import net.minecraft.client.Minecraft; 32 | import net.minecraft.client.gui.ScaledResolution; 33 | import net.minecraft.util.EnumChatFormatting; 34 | 35 | import java.awt.*; 36 | 37 | public class GuiButtonIsland extends GuiButtonScaleTransition { 38 | private static final float HOVERED_SCALE = 1.1F; 39 | private static final long SCALE_TRANSITION_DURATION = 400; 40 | final Island island; 41 | final ScaledGrid scaledGrid; 42 | 43 | public GuiButtonIsland(GuiFancyWarp parent, int buttonId, ScaledResolution res, Island island) { 44 | super(buttonId, ""); 45 | this.island = island; 46 | island.init(res); 47 | scaledXPosition = parent.getScaledGrid().getActualX(island.getGridX()); 48 | scaledYPosition = parent.getScaledGrid().getActualY(island.getGridY()); 49 | zLevel = island.getzLevel(); 50 | width = island.getWidth(); 51 | height = island.getHeight(); 52 | scaledGrid = new ScaledGrid(scaledXPosition, scaledYPosition, width, height, Warp.GRID_UNIT_WIDTH_FACTOR, true); 53 | displayString = EnumChatFormatting.GREEN + island.getName(); 54 | backgroundTextureLocation = island.getTextureLocation(); 55 | foregroundTextureLocation = island.getHoverEffectTextureLocation(); 56 | transition = new ScaleTransition(0, 1, 1); 57 | 58 | // Each line is drawn separately. Copy the colour code to all lines. 59 | if (displayString.contains("\n")) { 60 | displayString = displayString.replaceAll("\n", "\n" + EnumChatFormatting.GREEN); 61 | } 62 | } 63 | 64 | @Override 65 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 66 | if (visible) { 67 | float originalZ = zLevel; 68 | 69 | transitionStep(SCALE_TRANSITION_DURATION, HOVERED_SCALE); 70 | 71 | scaledGrid.setScaleFactor(transition.getCurrentScale()); 72 | scaledXPosition = scaledGrid.getGridStartX(); 73 | scaledYPosition = scaledGrid.getGridStartY(); 74 | scaledWidth = scaledGrid.getScaledDimension(width); 75 | scaledHeight = scaledGrid.getScaledDimension(height); 76 | 77 | if (hovered) { 78 | zLevel = 9; 79 | } 80 | 81 | drawButtonTexture(backgroundTextureLocation); 82 | if (hovered) { 83 | drawButtonForegroundLayer(foregroundTextureLocation); 84 | } 85 | 86 | if (Settings.shouldShowIslandLabels()) { 87 | drawDisplayString(mc, scaledWidth / 2F, scaledHeight); 88 | } 89 | 90 | if (Settings.isDebugModeEnabled() && Settings.shouldDrawBorders()) { 91 | drawBorder(Color.WHITE); 92 | } 93 | 94 | zLevel = originalZ; 95 | } 96 | } 97 | 98 | public Island getIsland() { 99 | return island; 100 | } 101 | 102 | public ScaledGrid getScaledGrid() { 103 | return scaledGrid; 104 | } 105 | 106 | public float getScaledXPosition() { 107 | return scaledXPosition; 108 | } 109 | 110 | public float getScaledYPosition() { 111 | return scaledYPosition; 112 | } 113 | 114 | public float getScaledWidth() { 115 | return scaledWidth; 116 | } 117 | 118 | public float getScaledHeight() { 119 | return scaledHeight; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonRegularWarpMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.RegularWarpMenuButton; 28 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.GridRectangle; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.ScaledGrid; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.transitions.ScaleTransition; 31 | import net.minecraft.client.Minecraft; 32 | import net.minecraft.client.gui.ScaledResolution; 33 | import net.minecraft.client.resources.I18n; 34 | import net.minecraft.util.EnumChatFormatting; 35 | 36 | @SuppressWarnings("FieldCanBeLocal") 37 | public class GuiButtonRegularWarpMenu extends GuiButtonScaleTransition { 38 | private static final float HOVERED_SCALE = 1.2F; 39 | private static final long SCALE_TRANSITION_DURATION = 500; 40 | // Far right edge 41 | private final int GRID_X; 42 | // Bottom edge 43 | private final int GRID_Y; 44 | 45 | public GuiButtonRegularWarpMenu(Layout layout, int buttonId, ScaledResolution res, ScaledGrid scaledGrid) { 46 | super(buttonId, EnumChatFormatting.GREEN + I18n.format(FancyWarpMenu.getFullLanguageKey("gui.buttons.regularWarpMenu"))); 47 | RegularWarpMenuButton regularWarpMenuButtonSettings = layout.getRegularWarpMenuButton(); 48 | regularWarpMenuButtonSettings.init(res); 49 | GRID_X = regularWarpMenuButtonSettings.getGridX(); 50 | GRID_Y = regularWarpMenuButtonSettings.getGridY(); 51 | width = regularWarpMenuButtonSettings.getWidth(); 52 | height = regularWarpMenuButtonSettings.getHeight(); 53 | // Above islands and warps 54 | zLevel = 20; 55 | buttonRectangle = new GridRectangle(scaledGrid, GRID_X, GRID_Y, width, height, false, true); 56 | scaledGrid.addRectangle("regularWarpMenuButton", buttonRectangle); 57 | backgroundTextureLocation = regularWarpMenuButtonSettings.getTextureLocation(); 58 | transition = new ScaleTransition(0, 1, 1); 59 | displayString = String.join("\n", Minecraft.getMinecraft().fontRendererObj.listFormattedStringToWidth(displayString, width * 3)); 60 | } 61 | 62 | @Override 63 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 64 | if (this.visible) { 65 | calculateHoverState(mouseX, mouseY); 66 | transitionStep(SCALE_TRANSITION_DURATION, HOVERED_SCALE); 67 | 68 | super.drawButton(mc, mouseX, mouseY); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonTimedLabel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import net.minecraft.client.Minecraft; 26 | import net.minecraft.client.gui.GuiButton; 27 | 28 | /** 29 | * This class allows the setting of a temporary button label that reverts to the original button label 30 | * after a given amount of time. 31 | */ 32 | public class GuiButtonTimedLabel extends GuiButton { 33 | private String originalButtonLabel; 34 | private long timedLabelExpiryTime; 35 | 36 | public GuiButtonTimedLabel(int buttonId, int x, int y, String buttonText) { 37 | super(buttonId, x, y, buttonText); 38 | } 39 | 40 | public GuiButtonTimedLabel(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText) { 41 | super(buttonId, x, y, widthIn, heightIn, buttonText); 42 | } 43 | 44 | /** 45 | * Reset the temporary label to the original label after {@link #timedLabelExpiryTime} has passed. 46 | */ 47 | @Override 48 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 49 | super.drawButton(mc, mouseX, mouseY); 50 | 51 | if (timedLabelExpiryTime > 0 && Minecraft.getSystemTime() > timedLabelExpiryTime) { 52 | timedLabelExpiryTime = -1; 53 | displayString = originalButtonLabel; 54 | } 55 | } 56 | 57 | /** 58 | * Set a temporary label text for this button that will revert to the original label text after the given 59 | * time has elapsed. 60 | * 61 | * @param labelText the temporary label text 62 | * @param time the time in milliseconds this label text should be shown for 63 | */ 64 | public void setTimedLabel(String labelText, int time) { 65 | timedLabelExpiryTime = Minecraft.getSystemTime() + time; 66 | originalButtonLabel = displayString; 67 | displayString = labelText; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/buttons/GuiButtonWarp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.buttons; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Warp; 28 | import ca.tirelesstraveler.fancywarpmenu.gui.grid.GridRectangle; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.transitions.ScaleTransition; 30 | import net.minecraft.client.Minecraft; 31 | 32 | public class GuiButtonWarp extends GuiButtonScaleTransition { 33 | /** The button of the island this warp belongs to */ 34 | private final GuiButtonIsland PARENT; 35 | private final Warp WARP; 36 | /** Whether the warp label should be drawn under the button */ 37 | private boolean drawWarpLabel; 38 | 39 | /** 40 | * x and y are relative to the top left corner of the parent island button. 41 | */ 42 | public GuiButtonWarp(int buttonId, GuiButtonIsland parent, Warp warp) { 43 | super(buttonId, ""); 44 | PARENT = parent; 45 | WARP = warp; 46 | buttonRectangle = new GridRectangle(parent.scaledGrid, warp.getGridX(), warp.getGridY(), warp.getWidth(), warp.getHeight(), true, false); 47 | parent.scaledGrid.addRectangle(warp.getDisplayName(), buttonRectangle); 48 | zLevel = 10; 49 | displayString = warp.getDisplayName(); 50 | drawWarpLabel = true; 51 | backgroundTextureLocation = WARP.getWarpTextureLocation(); 52 | foregroundTextureLocation = WARP.getWarpHoverEffectTextureLocation(); 53 | transition = new ScaleTransition(0, 0, 0); 54 | } 55 | 56 | /** 57 | * Draws this button to the screen. 58 | */ 59 | @Override 60 | public void drawButton(Minecraft mc, int mouseX, int mouseY) { 61 | if (visible) { 62 | float originalZ = zLevel; 63 | 64 | calculateHoverState(mouseX, mouseY); 65 | transition.setCurrentScale(PARENT.scaledGrid.getScaleFactor()); 66 | 67 | if (hovered) { 68 | zLevel = 19; 69 | } 70 | 71 | super.drawButton(mc, mouseX, mouseY); 72 | 73 | if (hovered) { 74 | drawButtonForegroundLayer(foregroundTextureLocation); 75 | } 76 | 77 | if (drawWarpLabel && (!Settings.shouldHideWarpLabelsUntilIslandHovered() || PARENT.isMouseOver())) { 78 | drawDisplayString(mc, buttonRectangle.getWidth() / 2F, buttonRectangle.getHeight()); 79 | } 80 | 81 | zLevel = originalZ; 82 | } 83 | } 84 | 85 | public String getWarpCommand() { 86 | return WARP.getWarpCommand(); 87 | } 88 | 89 | public int getWarpSlotIndex() { 90 | return WARP.getSlotIndex(); 91 | } 92 | 93 | public Island getIsland() { 94 | return PARENT.island; 95 | } 96 | 97 | public Warp getWarp() { 98 | return WARP; 99 | } 100 | 101 | public void setDrawWarpLabel(boolean drawWarpLabel) { 102 | this.drawWarpLabel = drawWarpLabel; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/grid/GridRectangle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.grid; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.gui.buttons.GuiButtonIsland; 26 | 27 | /** 28 | * A rectangle placed on a {@code ScaledGrid}. These rectangles serve as bounding boxes for the GUI elements placed on 29 | * the grid, providing coordinates and dimensions to be used during rendering. This class does not cover using a 30 | * {@code ScaledGrid} placed within a {@code ScaledGrid}. For an example of that usage, see {@link GuiButtonIsland} 31 | * 32 | * @see ScaledGrid 33 | */ 34 | public class GridRectangle { 35 | protected transient final ScaledGrid scaledGrid; 36 | protected int gridX; 37 | protected int gridY; 38 | /** x position in pixels of the left edge */ 39 | protected float xPosition; 40 | /** y position in pixels of the top edge */ 41 | protected float yPosition; 42 | /** scaled x position of the left edge */ 43 | protected float scaledXPosition; 44 | /** scaled y position of the top edge */ 45 | protected float scaledYPosition; 46 | protected float width; 47 | protected float height; 48 | protected float scaledWidth; 49 | protected float scaledHeight; 50 | /** If {@code true}, multiply the position by the scale factor. If {@code false}, leave position as is. */ 51 | protected boolean scalePosition; 52 | /** If {@code true}, shift the position so the rectangle looks like it's being expanded from the centre instead of the top left when scaled. If {@code false}, leave position as is. This works only when {@code scalePosition} is {@code false}. */ 53 | protected boolean centerPositionWhenScaled; 54 | 55 | public GridRectangle(ScaledGrid scaledGrid, int gridX, int gridY, float width, float height, boolean scalePosition, boolean centerPositionWhenScaled) { 56 | this.scaledGrid = scaledGrid; 57 | this.gridX = gridX; 58 | this.gridY = gridY; 59 | this.xPosition = scaledGrid.getActualX(gridX); 60 | this.yPosition = scaledGrid.getActualY(gridY); 61 | this.width = width; 62 | this.height = height; 63 | this.scalePosition = scalePosition; 64 | this.centerPositionWhenScaled = centerPositionWhenScaled; 65 | } 66 | 67 | public void scale(float scaleFactor) { 68 | scaledWidth = width * scaleFactor; 69 | scaledHeight = height * scaleFactor; 70 | 71 | if (scalePosition) { 72 | scaledXPosition = scaledGrid.getActualX(gridX); 73 | scaledYPosition = scaledGrid.getActualY(gridY); 74 | } else if (centerPositionWhenScaled) { 75 | scaledXPosition = xPosition - (scaledWidth - width) / 2; 76 | scaledYPosition = yPosition - (scaledHeight - height) / 2; 77 | } 78 | } 79 | 80 | public float getXPosition() { 81 | if (scalePosition || centerPositionWhenScaled) { 82 | return scaledXPosition; 83 | } else { 84 | return xPosition; 85 | } 86 | } 87 | 88 | public float getYPosition() { 89 | if (scalePosition || centerPositionWhenScaled) { 90 | return scaledYPosition; 91 | } else { 92 | return yPosition; 93 | } 94 | } 95 | 96 | public float getWidth() { 97 | return scaledWidth; 98 | } 99 | 100 | public float getHeight() { 101 | return scaledHeight; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return "GridRectangle{" + 107 | "gridX=" + gridX + 108 | ", gridY=" + gridY + 109 | ", xPosition=" + xPosition + 110 | ", yPosition=" + yPosition + 111 | ", scaledXPosition=" + scaledXPosition + 112 | ", scaledYPosition=" + scaledYPosition + 113 | ", width=" + width + 114 | ", height=" + height + 115 | ", scaledWidth=" + scaledWidth + 116 | ", scaledHeight=" + scaledHeight + 117 | ", scalePosition=" + scalePosition + 118 | ", centerPositionWhenScaled=" + centerPositionWhenScaled + 119 | '}'; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/transitions/ScaleTransition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.transitions; 24 | 25 | /** 26 | * Transition used to change a GUI element's size as time passes 27 | */ 28 | public class ScaleTransition extends Transition { 29 | private final float START_SCALE; 30 | private final float END_SCALE; 31 | private float currentScale; 32 | 33 | public ScaleTransition(long duration, float startScale, float endScale) { 34 | super(duration); 35 | this.START_SCALE = startScale; 36 | this.END_SCALE = endScale; 37 | this.currentScale = startScale; 38 | } 39 | 40 | @Override 41 | public void step() { 42 | super.step(); 43 | currentScale = START_SCALE + (END_SCALE - START_SCALE) * progress; 44 | } 45 | 46 | public float getStartScale() { 47 | return START_SCALE; 48 | } 49 | 50 | public float getEndScale() { 51 | return END_SCALE; 52 | } 53 | 54 | public float getCurrentScale() { 55 | return currentScale; 56 | } 57 | 58 | public void setCurrentScale(float scale) { 59 | currentScale = scale; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/gui/transitions/Transition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.gui.transitions; 24 | 25 | import net.minecraft.client.Minecraft; 26 | 27 | /** 28 | * This base class tracks a start time, end time, and the current system time's difference from the end time. 29 | * Subclasses are meant to extend this with GUI attributes whose values will change as time passes. 30 | */ 31 | public class Transition { 32 | protected long startTime; 33 | protected long endTime; 34 | protected long duration; 35 | protected long currentTime; 36 | protected float progress; 37 | protected boolean finished; 38 | 39 | public Transition(long duration) { 40 | startTime = Minecraft.getSystemTime(); 41 | this.duration = duration; 42 | endTime = startTime + duration; 43 | finished = false; 44 | } 45 | 46 | public float getProgress() { 47 | return progress; 48 | } 49 | 50 | public boolean isFinished() { 51 | return finished; 52 | } 53 | 54 | /** 55 | * Recalculate the progress of the transition using the current system time 56 | */ 57 | public void step() { 58 | if (!finished) { 59 | currentTime = Minecraft.getSystemTime(); 60 | progress = (float) (currentTime - startTime) / duration; 61 | 62 | if (currentTime >= endTime) { 63 | progress = 1; 64 | finished = true; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/ChatListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.listeners; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.WarpCommandVariant; 28 | import ca.tirelesstraveler.fancywarpmenu.gui.GuiFancyWarp; 29 | import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; 30 | import ca.tirelesstraveler.fancywarpmenu.utils.ChatUtils; 31 | import net.minecraft.client.Minecraft; 32 | import net.minecraft.client.gui.GuiChat; 33 | import net.minecraft.client.gui.GuiScreen; 34 | import net.minecraft.util.ChatComponentTranslation; 35 | import net.minecraft.util.ChatStyle; 36 | import net.minecraft.util.EnumChatFormatting; 37 | import net.minecraft.util.IChatComponent; 38 | import net.minecraftforge.client.event.ClientChatReceivedEvent; 39 | import net.minecraftforge.client.event.GuiOpenEvent; 40 | import net.minecraftforge.client.event.GuiScreenEvent; 41 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 42 | import org.lwjgl.input.Keyboard; 43 | import org.lwjgl.input.Mouse; 44 | 45 | import java.util.List; 46 | import java.util.Locale; 47 | 48 | public class ChatListener { 49 | private final Minecraft mc; 50 | private boolean chatMessageSendDetected; 51 | 52 | public ChatListener() { 53 | mc = Minecraft.getMinecraft(); 54 | } 55 | 56 | @SubscribeEvent 57 | public void onChatMessageReceived(ClientChatReceivedEvent event) { 58 | // type 0 is a standard chat message 59 | if (event.type == 0 && FancyWarpMenuState.isFancyWarpMenuOpen()) { 60 | String unformattedText = event.message.getUnformattedText(); 61 | 62 | if (FancyWarpMenu.getSkyBlockConstants().getWarpMessages().getWarpFailMessages().containsKey(unformattedText)) { 63 | String failMessageKey = FancyWarpMenu.getSkyBlockConstants().getWarpMessages().getWarpFailMessages().get(unformattedText); 64 | ((GuiFancyWarp) mc.currentScreen).onWarpFail(failMessageKey); 65 | } 66 | } 67 | } 68 | 69 | @SubscribeEvent 70 | public void onGuiOpen(GuiOpenEvent event) { 71 | if (chatMessageSendDetected && mc.currentScreen instanceof GuiChat && event.gui == null) { 72 | chatMessageSendDetected = false; 73 | 74 | List sentMessages = mc.ingameGUI.getChatGUI().getSentMessages(); 75 | 76 | if (!sentMessages.isEmpty()) { 77 | checkChatMessageForReminder(sentMessages.get(sentMessages.size() - 1)); 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Copies the throwable provided when the component from 84 | * {@link ChatUtils#sendErrorMessageWithCopyableThrowable(String, Throwable)} is clicked. 85 | * 86 | * @param event the mouse event 87 | */ 88 | @SubscribeEvent 89 | public void mouseClicked(GuiScreenEvent.MouseInputEvent.Pre event) { 90 | // Left mouse button down 91 | if (event.gui instanceof GuiChat && Mouse.getEventButton() == 0 && Mouse.getEventButtonState()) { 92 | IChatComponent chatComponent = mc.ingameGUI.getChatGUI().getChatComponent(Mouse.getX(), Mouse.getY()); 93 | 94 | if (chatComponent != null && chatComponent.getChatStyle() != null) { 95 | ChatStyle chatStyle = chatComponent.getChatStyle(); 96 | String insertion = chatStyle.getInsertion(); 97 | 98 | if (insertion != null && insertion.equals(ChatUtils.COPY_TO_CLIPBOARD_TRANSLATION_KEY) 99 | && chatStyle.getChatClickEvent() != null) { 100 | String clickEventValue = chatStyle.getChatClickEvent().getValue(); 101 | 102 | if (clickEventValue != null) { 103 | String copiedMessageTranslationKey = "fancywarpmenu.gui.buttons.copyToClipboard.copied"; 104 | 105 | GuiScreen.setClipboardString(clickEventValue); 106 | ChatUtils.sendMessageWithModNamePrefix(new ChatComponentTranslation(copiedMessageTranslationKey) 107 | .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.GREEN))); 108 | event.setCanceled(true); 109 | } 110 | } 111 | } 112 | } 113 | } 114 | 115 | @SubscribeEvent 116 | public void keyTyped(GuiScreenEvent.KeyboardInputEvent event) { 117 | if (event.gui instanceof GuiChat && 118 | (Keyboard.getEventKey() == Keyboard.KEY_RETURN || Keyboard.getEventKey() == Keyboard.KEY_NUMPADENTER) && 119 | Keyboard.getEventKeyState()) { 120 | chatMessageSendDetected = true; 121 | } 122 | } 123 | 124 | /** 125 | * If the reminder feature is enabled, check a given chat message for a warp command variant. 126 | * If the message is a warp command variant, remind the player to use the Fancy Warp Menu instead of commands. 127 | * 128 | * @param sentChatMessage the chat message that was just sent 129 | * @see Settings#shouldSuggestWarpMenuOnWarpCommand() 130 | */ 131 | private void checkChatMessageForReminder(String sentChatMessage) { 132 | if (Settings.shouldSuggestWarpMenuOnWarpCommand() && getWarpCommandVariant(sentChatMessage) != null) { 133 | sendReminderToUseFancyMenu(); 134 | } 135 | } 136 | 137 | /** 138 | * Checks if a given command is the warp command or any of its variants and returns the corresponding 139 | * {@code WarpCommandVariant} object if one is found. 140 | * 141 | * @param command the command the player sent 142 | * @return a {@link WarpCommandVariant} if one with the same command is found, or {@code null} otherwise 143 | */ 144 | private WarpCommandVariant getWarpCommandVariant(String command) { 145 | // Trim off the slash and all arguments 146 | String baseCommand = command.toLowerCase(Locale.US).substring(1).split(" ")[0]; 147 | 148 | for (WarpCommandVariant commandVariant : FancyWarpMenu.getSkyBlockConstants().getWarpCommandVariants()) { 149 | if (commandVariant.getCommand().equals(baseCommand)) { 150 | return commandVariant; 151 | } 152 | } 153 | 154 | return null; 155 | } 156 | 157 | private void sendReminderToUseFancyMenu() { 158 | ChatUtils.sendMessageWithModNamePrefix(new ChatComponentTranslation(FancyWarpMenu 159 | .getFullLanguageKey("messages.useWarpMenuInsteadOfCommand")) 160 | .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/InventoryChangeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.listeners; 24 | 25 | import net.minecraft.inventory.IInvBasic; 26 | import net.minecraft.inventory.InventoryBasic; 27 | 28 | import java.util.function.Consumer; 29 | 30 | /** 31 | * A simple listener that calls a callback when an item changes in the inventory it is assigned to. 32 | */ 33 | public class InventoryChangeListener implements IInvBasic { 34 | private final Consumer CALLBACK; 35 | 36 | public InventoryChangeListener(Consumer callback) { 37 | CALLBACK = callback; 38 | } 39 | 40 | 41 | @Override 42 | public void onInventoryChanged(InventoryBasic inventoryBasic) { 43 | CALLBACK.accept(inventoryBasic); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/SkyBlockJoinListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.listeners; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.state.GameState; 26 | import io.netty.channel.ChannelHandler; 27 | import net.minecraft.client.Minecraft; 28 | import net.minecraft.client.entity.EntityPlayerSP; 29 | import net.minecraft.scoreboard.Scoreboard; 30 | import net.minecraftforge.client.event.ClientChatReceivedEvent; 31 | import net.minecraftforge.event.world.WorldEvent; 32 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 33 | import net.minecraftforge.fml.common.network.FMLNetworkEvent; 34 | import org.apache.logging.log4j.LogManager; 35 | import org.apache.logging.log4j.Logger; 36 | 37 | /** 38 | * Forge event and packet listener that detects when the player joins/leaves SkyBlock 39 | */ 40 | @ChannelHandler.Sharable 41 | public class SkyBlockJoinListener { 42 | private static final String SERVER_BRAND_START = "Hypixel BungeeCord"; 43 | private static final int SCOREBOARD_CHECK_TIME_OUT = 5000; 44 | 45 | private static final Logger logger = LogManager.getLogger(); 46 | private boolean serverBrandChecked; 47 | private boolean onHypixel; 48 | private boolean scoreboardChecked; 49 | private long lastWorldSwitchTime; 50 | 51 | @SubscribeEvent 52 | public void onClientDisconnect(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) { 53 | if (onHypixel) { 54 | serverBrandChecked = false; 55 | onHypixel = false; 56 | GameState.setOnSkyBlock(false); 57 | logger.debug("Disconnected from Hypixel."); 58 | } 59 | } 60 | 61 | @SubscribeEvent 62 | public void onWorldLoad(WorldEvent.Load event) { 63 | // Reset on world switch 64 | lastWorldSwitchTime = Minecraft.getSystemTime(); 65 | scoreboardChecked = false; 66 | GameState.setOnSkyBlock(false); 67 | } 68 | 69 | @SubscribeEvent 70 | public void onChatMessageReceived(ClientChatReceivedEvent event) { 71 | if (!serverBrandChecked || onHypixel && !scoreboardChecked) { 72 | // type 0 is a standard chat message 73 | if (event.type == 0) { 74 | EntityPlayerSP thePlayer = Minecraft.getMinecraft().thePlayer; 75 | String brand = thePlayer.getClientBrand(); 76 | 77 | if (!serverBrandChecked) { 78 | onHypixel = brand != null && brand.startsWith(SERVER_BRAND_START); 79 | serverBrandChecked = true; 80 | 81 | if (onHypixel) { 82 | logger.debug("Player joined Hypixel."); 83 | } 84 | } 85 | 86 | if (onHypixel && !scoreboardChecked) { 87 | Scoreboard scoreboard = thePlayer.getWorldScoreboard(); 88 | boolean newSkyBlockState = scoreboard != null && scoreboard.getObjective("SBScoreboard") != null; 89 | 90 | if (newSkyBlockState != GameState.isOnSkyBlock()) { 91 | if (newSkyBlockState) { 92 | logger.debug("Player joined SkyBlock."); 93 | } else { 94 | logger.debug("Player left SkyBlock."); 95 | } 96 | 97 | GameState.setOnSkyBlock(newSkyBlockState); 98 | scoreboardChecked = true; 99 | } 100 | 101 | if (Minecraft.getSystemTime() - lastWorldSwitchTime > SCOREBOARD_CHECK_TIME_OUT) { 102 | scoreboardChecked = true; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/listeners/WarpMenuListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.listeners; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants; 28 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 29 | import ca.tirelesstraveler.fancywarpmenu.gui.FancyWarpMenuConfigScreen; 30 | import ca.tirelesstraveler.fancywarpmenu.gui.GuiFastTravel; 31 | import ca.tirelesstraveler.fancywarpmenu.gui.GuiRiftFastTravel; 32 | import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; 33 | import ca.tirelesstraveler.fancywarpmenu.state.GameState; 34 | import ca.tirelesstraveler.fancywarpmenu.utils.GameChecks; 35 | import net.minecraft.client.Minecraft; 36 | import net.minecraft.client.gui.inventory.GuiChest; 37 | import net.minecraft.client.resources.IResourceManager; 38 | import net.minecraft.client.resources.IResourceManagerReloadListener; 39 | import net.minecraft.inventory.ContainerChest; 40 | import net.minecraft.inventory.IInventory; 41 | import net.minecraftforge.client.event.GuiOpenEvent; 42 | import net.minecraftforge.fml.client.event.ConfigChangedEvent; 43 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 44 | import net.minecraftforge.fml.common.gameevent.InputEvent; 45 | import org.apache.logging.log4j.LogManager; 46 | import org.apache.logging.log4j.Logger; 47 | 48 | /** 49 | * General purpose event listener 50 | */ 51 | public class WarpMenuListener implements IResourceManagerReloadListener { 52 | private static final Minecraft mc; 53 | private static final FancyWarpMenu modInstance; 54 | private static final Logger logger; 55 | /** The minimum time in milliseconds after a hotkey press before the player can use the hotkey again*/ 56 | private static final int HOTKEY_PRESS_DELAY = 2000; 57 | 58 | /** 59 | * Time the user last pressed the fancy warp menu hotkey, used to prevent command spamming from 60 | * spam pressing the hotkey 61 | */ 62 | private long lastWarpMenuHotkeyPress; 63 | 64 | static { 65 | mc = Minecraft.getMinecraft(); 66 | modInstance = FancyWarpMenu.getInstance(); 67 | logger = LogManager.getLogger(); 68 | } 69 | 70 | @SubscribeEvent 71 | public void onGuiOpen(GuiOpenEvent event) { 72 | /* 73 | Minecraft closes the current screen after executing a command, meaning any GuiScreen opened by the command is closed. 74 | This section interrupts the closing of the current screen to get around this behavior. 75 | */ 76 | if (event.gui == null) { 77 | if (FancyWarpMenuState.isOpenConfigMenuRequested()) { 78 | event.gui = new FancyWarpMenuConfigScreen(null); 79 | FancyWarpMenuState.setOpenConfigMenuRequested(false); 80 | } 81 | } else if (GameState.isOnSkyBlock() && event.gui instanceof GuiChest) { 82 | IInventory playerInventory = mc.thePlayer.inventory; 83 | IInventory chestInventory = ((ContainerChest) ((GuiChest) event.gui).inventorySlots).getLowerChestInventory(); 84 | 85 | Menu currentMenu = GameChecks.determineOpenMenu(chestInventory); 86 | 87 | if (currentMenu == Menu.FAST_TRAVEL) { 88 | event.gui = new GuiFastTravel(playerInventory, chestInventory, FancyWarpMenuState.getOverworldLayout()); 89 | } else if (currentMenu == Menu.PORHTAL) { 90 | event.gui = new GuiRiftFastTravel(playerInventory, chestInventory, FancyWarpMenuState.getRiftLayout()); 91 | } 92 | } 93 | } 94 | 95 | @SubscribeEvent 96 | public void keyTyped(InputEvent.KeyInputEvent event) { 97 | if (Settings.isWarpMenuEnabled() && GameState.isOnSkyBlock() && 98 | FancyWarpMenu.getKeyBindingOpenWarpMenu().isPressed() && 99 | Minecraft.getSystemTime() - lastWarpMenuHotkeyPress > HOTKEY_PRESS_DELAY) { 100 | lastWarpMenuHotkeyPress = Minecraft.getSystemTime(); 101 | mc.thePlayer.sendChatMessage(SkyBlockConstants.WARP_COMMAND_BASE); 102 | } 103 | } 104 | 105 | @SubscribeEvent 106 | public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent event) { 107 | if (event.modID.equals(modInstance.getModId())) { 108 | Settings.syncConfig(false); 109 | } 110 | } 111 | 112 | @Override 113 | public void onResourceManagerReload(IResourceManager resourceManager) { 114 | FancyWarpMenu.getInstance().reloadLayouts(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/LayoutLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.resourceloaders; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.layout.*; 26 | import ca.tirelesstraveler.fancywarpmenu.state.FancyWarpMenuState; 27 | import com.google.gson.stream.JsonReader; 28 | import net.minecraft.client.Minecraft; 29 | import net.minecraft.client.renderer.texture.TextureUtil; 30 | import net.minecraft.client.resources.IResource; 31 | import net.minecraft.client.resources.IResourceManager; 32 | import net.minecraft.crash.CrashReport; 33 | import net.minecraft.util.ReportedException; 34 | import net.minecraft.util.ResourceLocation; 35 | import org.apache.commons.lang3.tuple.Pair; 36 | 37 | import java.awt.image.BufferedImage; 38 | import java.io.IOException; 39 | import java.io.InputStream; 40 | import java.io.InputStreamReader; 41 | 42 | public class LayoutLoader extends ResourceLoader { 43 | public static final ResourceLocation OVERWORLD_LAYOUT_LOCATION = new ResourceLocation("fancywarpmenu", 44 | "data/layout.json"); 45 | public static final ResourceLocation RIFT_LAYOUT_LOCATION = new ResourceLocation("fancywarpmenu", "data/riftLayout.json"); 46 | 47 | public static Layout loadLayout(ResourceLocation resourceLocation) { 48 | try { 49 | IResource layoutResource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation); 50 | 51 | try (InputStream stream = layoutResource.getInputStream(); 52 | JsonReader reader = new JsonReader(new InputStreamReader(stream))) { 53 | Layout layout = gson.fromJson(reader, Layout.class); 54 | Layout.validateLayout(layout); 55 | 56 | // Layout background texture 57 | layout.setBackgroundTextureLocation(); 58 | 59 | // Warp icon 60 | WarpIcon warpIcon = layout.getWarpIcon(); 61 | Warp.setWarpIcon(warpIcon); 62 | warpIcon.init(); 63 | Pair warpIconDimensions = getTextureDimensions(warpIcon.getTextureLocation()); 64 | warpIcon.setTextureDimensions(warpIconDimensions.getLeft(), warpIconDimensions.getRight()); 65 | 66 | // Config and regular warp menu button icon dimensions 67 | ConfigButton configButton = layout.getConfigButton(); 68 | RegularWarpMenuButton regularWarpMenuButton = layout.getRegularWarpMenuButton(); 69 | 70 | Pair configButtonIconDimensions = getTextureDimensions(configButton.getTextureLocation()); 71 | configButton.setTextureDimensions(configButtonIconDimensions.getLeft(), configButtonIconDimensions.getRight()); 72 | Pair regularWarpMenuButtonIconDimensions = getTextureDimensions(regularWarpMenuButton.getTextureLocation()); 73 | regularWarpMenuButton.setTextureDimensions(regularWarpMenuButtonIconDimensions.getLeft(), regularWarpMenuButtonIconDimensions.getRight()); 74 | 75 | // Island texture dimensions and hover effect texture 76 | for (Island island : layout.getIslandList()) { 77 | Pair islandTextureDimensions; 78 | 79 | island.setTextureLocation(); 80 | islandTextureDimensions = getTextureDimensions(island.getTextureLocation()); 81 | island.setTextureDimensions(islandTextureDimensions.getLeft(), islandTextureDimensions.getRight()); 82 | 83 | if (island.getHoverEffectTexturePath() != null) { 84 | island.setHoverEffectTextureLocation(); 85 | } 86 | } 87 | 88 | return layout; 89 | } catch (RuntimeException e) { 90 | boolean fatal = FancyWarpMenuState.getOverworldLayout() == null; 91 | 92 | handleResourceLoadException(layoutResource, fatal, e); 93 | return null; 94 | } 95 | } catch (IOException e) { 96 | boolean fatal = FancyWarpMenuState.getOverworldLayout() == null; 97 | 98 | handleGetResourceException(resourceLocation.toString(), fatal, e); 99 | return null; 100 | } 101 | } 102 | 103 | private static Pair getTextureDimensions(ResourceLocation resourceLocation) { 104 | IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager(); 105 | 106 | try (InputStream textureStream = resourceManager.getResource(resourceLocation).getInputStream()) { 107 | BufferedImage bufferedImage = TextureUtil.readBufferedImage(textureStream); 108 | return Pair.of(bufferedImage.getWidth(), bufferedImage.getHeight()); 109 | } catch (IOException e) { 110 | throw new ReportedException(CrashReport.makeCrashReport(e, 111 | String.format("Failed to read texture \"%s\"", resourceLocation))); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/ResourceLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.resourceloaders; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.utils.ChatUtils; 27 | import com.google.gson.Gson; 28 | import com.google.gson.GsonBuilder; 29 | import net.minecraft.client.Minecraft; 30 | import net.minecraft.client.resources.IResource; 31 | import net.minecraft.client.resources.ResourcePackRepository; 32 | import net.minecraft.crash.CrashReport; 33 | import net.minecraft.crash.CrashReportCategory; 34 | import net.minecraft.util.ReportedException; 35 | import org.apache.logging.log4j.LogManager; 36 | import org.apache.logging.log4j.Logger; 37 | 38 | import java.io.IOException; 39 | 40 | // TODO: Localize long error messages? 41 | public abstract class ResourceLoader { 42 | public static final Gson gson = new GsonBuilder() 43 | .setPrettyPrinting() 44 | .create(); 45 | protected static final Logger logger = LogManager.getLogger(); 46 | 47 | protected static void handleResourceLoadException(IResource resource, boolean fatal, RuntimeException e) { 48 | ResourcePackRepository.Entry resourcePackEntry = Minecraft.getMinecraft().getResourcePackRepository().getRepositoryEntries().stream().filter( 49 | entry -> entry.getResourcePackName().equals(resource.getResourcePackName())).findFirst().orElse(null); 50 | String resourcePackName; 51 | String resourcePackDescription; 52 | 53 | if (resourcePackEntry != null) { 54 | resourcePackName = resource.getResourcePackName(); 55 | resourcePackDescription = resourcePackEntry.getTexturePackDescription(); 56 | } else { 57 | resourcePackName = FancyWarpMenu.getInstance().getModContainer().getName() + " " + FancyWarpMenu.getInstance().getModContainer().getVersion(); 58 | resourcePackDescription = "Built-in resource pack"; 59 | } 60 | 61 | if (!fatal) { 62 | StringBuilder stringBuilder = new StringBuilder("Your Fancy Warp Menu resource pack may be outdated."); 63 | stringBuilder.append("\n").append(String.format("Resource loading failed: %s", e.getMessage())); 64 | stringBuilder.append("\n").append(String.format("Resource Path: %s", resource.getResourceLocation().toString())); 65 | stringBuilder.append("\n").append("Resource Pack Details:"); 66 | stringBuilder.append("\n").append("Name: ").append(resourcePackName); 67 | stringBuilder.append("\n").append("Description: ").append(resourcePackDescription); 68 | 69 | if (resourcePackEntry != null) { 70 | stringBuilder.append("\n").append("File: ").append(resourcePackEntry); 71 | } 72 | 73 | logger.error(stringBuilder, e); 74 | 75 | if (Minecraft.getMinecraft().ingameGUI != null) { 76 | ChatUtils.sendErrorMessageWithCopyableThrowable(stringBuilder.toString(), e); 77 | } 78 | } else { 79 | CrashReport crashReport = new CrashReport("Your Fancy Warp Menu resource pack may be outdated", e); 80 | CrashReportCategory resourceDetails = crashReport.makeCategory("Resource"); 81 | CrashReportCategory resourcePackDetails = crashReport.makeCategory("Resource Pack"); 82 | 83 | resourceDetails.addCrashSection("Path", resource.getResourceLocation().toString()); 84 | 85 | resourcePackDetails.addCrashSection("Name", resourcePackName); 86 | resourcePackDetails.addCrashSection("Description", resourcePackDescription); 87 | 88 | if (resourcePackEntry != null) { 89 | resourcePackDetails.addCrashSection("File", resourcePackEntry.toString()); 90 | } 91 | 92 | throw new ReportedException(crashReport); 93 | } 94 | } 95 | 96 | protected static void handleGetResourceException(String resourcePath, boolean fatal, IOException e) { 97 | if (!fatal) { 98 | StringBuilder stringBuilder = new StringBuilder("Your Fancy Warp Menu resource pack may be outdated."); 99 | stringBuilder.append("\n").append(String.format("Resource loading failed: %s", e.getMessage())); 100 | stringBuilder.append("\n").append(String.format("Resource Path: %s", resourcePath)); 101 | 102 | logger.error(stringBuilder, e); 103 | 104 | if (Minecraft.getMinecraft().ingameGUI != null) { 105 | ChatUtils.sendErrorMessageWithCopyableThrowable(stringBuilder.toString(), e); 106 | } 107 | } else { 108 | CrashReport crashReport = new CrashReport("Your Fancy Warp Menu resource pack may be outdated", e); 109 | CrashReportCategory resourceDetails = crashReport.makeCategory("Resource"); 110 | resourceDetails.addCrashSection("Path", resourcePath); 111 | throw new ReportedException(crashReport); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/resourceloaders/SkyBlockConstantsLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.resourceloaders; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants; 27 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; 28 | import com.google.gson.stream.JsonReader; 29 | import net.minecraft.client.Minecraft; 30 | import net.minecraft.client.resources.IResource; 31 | import net.minecraft.util.ResourceLocation; 32 | 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.io.InputStreamReader; 36 | import java.util.Comparator; 37 | import java.util.List; 38 | 39 | public class SkyBlockConstantsLoader extends ResourceLoader { 40 | private static final ResourceLocation SKY_BLOCK_CONSTANTS_LOCATION = new ResourceLocation("fancywarpmenu", 41 | "data/skyBlockConstants.jsonc"); 42 | 43 | /** 44 | * Creates a {@link SkyBlockConstants} instance from {@code SKY_BLOCK_CONSTANTS_LOCATION}. 45 | * Match conditions in {@code SkyBlockConstants#menuMatchingMap} are sorted by ascending item slot index. 46 | * 47 | * @return the created {@code SkyBlockConstants} instance 48 | */ 49 | public static SkyBlockConstants loadSkyBlockConstants() { 50 | try { 51 | IResource skyBlockConstantsResource = Minecraft.getMinecraft().getResourceManager().getResource(SKY_BLOCK_CONSTANTS_LOCATION); 52 | 53 | try (InputStream stream = skyBlockConstantsResource.getInputStream(); 54 | JsonReader reader = new JsonReader(new InputStreamReader(stream))) { 55 | SkyBlockConstants skyBlockConstants = gson.fromJson(reader, SkyBlockConstants.class); 56 | 57 | for (List matchConditionList : skyBlockConstants.getMenuMatchingMap().values()) { 58 | matchConditionList.sort(Comparator.comparing(ItemMatchCondition::getInventorySlotIndex)); 59 | } 60 | 61 | SkyBlockConstants.validateSkyBlockConstants(skyBlockConstants); 62 | 63 | return skyBlockConstants; 64 | } catch (RuntimeException e) { 65 | boolean fatal = FancyWarpMenu.getSkyBlockConstants() == null; 66 | 67 | handleResourceLoadException(skyBlockConstantsResource, fatal, e); 68 | return null; 69 | } 70 | } catch (IOException e) { 71 | boolean fatal = FancyWarpMenu.getSkyBlockConstants() == null; 72 | 73 | handleGetResourceException(SKY_BLOCK_CONSTANTS_LOCATION.toString(), fatal, e); 74 | return null; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/state/EnvironmentDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.state; 24 | 25 | /** 26 | * Information about the local environment at runtime, for constants and compatibility with other mods 27 | */ 28 | public class EnvironmentDetails { 29 | public static final String SUPPORT_LINK = "https://discord.gg/tXFf9umfA9"; 30 | private static boolean deobfuscatedEnvironment; 31 | /** 32 | * Whether the Sk1er Patcher mod is installed. Patcher's GUI scale feature causes the Fancy Warp Menu to not be 33 | * scaled properly so the menu must be initialized differently when Patcher is present. 34 | * */ 35 | private static boolean patcherInstalled; 36 | 37 | private EnvironmentDetails() { 38 | } 39 | 40 | public static boolean isDeobfuscatedEnvironment() { 41 | return deobfuscatedEnvironment; 42 | } 43 | 44 | public static boolean isPatcherInstalled() { 45 | return patcherInstalled; 46 | } 47 | 48 | public static void setDeobfuscatedEnvironment(boolean deobfuscatedEnvironment) { 49 | EnvironmentDetails.deobfuscatedEnvironment = deobfuscatedEnvironment; 50 | } 51 | 52 | public static void setPatcherInstalled(boolean patcherInstalled) { 53 | EnvironmentDetails.patcherInstalled = patcherInstalled; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/state/FancyWarpMenuState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.state; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Layout; 26 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 27 | import ca.tirelesstraveler.fancywarpmenu.gui.GuiFancyWarp; 28 | import net.minecraft.client.Minecraft; 29 | 30 | /** 31 | * This class stores the current state of the Fancy Warp Menu. 32 | */ 33 | public class FancyWarpMenuState { 34 | private static Layout overworldLayout; 35 | private static Layout riftLayout; 36 | private static boolean openConfigMenuRequested; 37 | 38 | /** 39 | * Gets the layout corresponding to the given SkyBlock menu. 40 | * 41 | * @param menu the SkyBlock menu to get a layout for 42 | * @return {@code riftLayout} if {@code Menu.PORHTAL} is provided, {@code overworldLayout} otherwise 43 | */ 44 | public static Layout getLayoutForMenu(Menu menu) { 45 | if (menu == Menu.PORHTAL) { 46 | return getRiftLayout(); 47 | } else { 48 | return getOverworldLayout(); 49 | } 50 | } 51 | 52 | public static Layout getOverworldLayout() { 53 | return overworldLayout; 54 | } 55 | 56 | public static Layout getRiftLayout() { 57 | return riftLayout; 58 | } 59 | 60 | public static boolean isFancyWarpMenuOpen() { 61 | return Minecraft.getMinecraft().currentScreen instanceof GuiFancyWarp; 62 | } 63 | 64 | public static boolean isOpenConfigMenuRequested() { 65 | return openConfigMenuRequested; 66 | } 67 | 68 | public static void setOverworldLayout(Layout overworldLayout) { 69 | FancyWarpMenuState.overworldLayout = overworldLayout; 70 | } 71 | 72 | public static void setRiftLayout(Layout riftLayout) { 73 | FancyWarpMenuState.riftLayout = riftLayout; 74 | } 75 | 76 | public static void setOpenConfigMenuRequested(boolean openConfigMenuRequested) { 77 | FancyWarpMenuState.openConfigMenuRequested = openConfigMenuRequested; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/state/GameState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.state; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 26 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 27 | 28 | /** 29 | * This class stores information about the state of the SkyBlock game the player is currently in. 30 | */ 31 | public class GameState { 32 | /** 33 | * Whether the player is currently on SkyBlock 34 | */ 35 | private static boolean onSkyBlock; 36 | /** 37 | * The current stage of the in-game season, can be "Early", mid (null), or "Late". 38 | */ 39 | private static String seasonStage; 40 | /** 41 | * The current in-game season 42 | */ 43 | private static String season; 44 | /** 45 | * Current in-game menu the player has open 46 | */ 47 | private static Menu currentMenu; 48 | 49 | public static boolean isOnSkyBlock() { 50 | return onSkyBlock || Settings.shouldSkipSkyBlockCheck(); 51 | } 52 | 53 | public static void setOnSkyBlock(boolean onSkyBlock) { 54 | GameState.onSkyBlock = onSkyBlock; 55 | } 56 | 57 | public static String getSeasonStage() { 58 | return seasonStage; 59 | } 60 | 61 | public static void setSeasonStage(String seasonStage) { 62 | GameState.seasonStage = seasonStage; 63 | } 64 | 65 | public static String getSeason() { 66 | return season; 67 | } 68 | 69 | public static void setSeason(String season) { 70 | GameState.season = season; 71 | } 72 | 73 | public static Menu getCurrentMenu() { 74 | return currentMenu; 75 | } 76 | 77 | public static void setCurrentMenu(Menu currentMenu) { 78 | GameState.currentMenu = currentMenu; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/ChatUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.utils; 24 | 25 | import net.minecraft.client.Minecraft; 26 | import net.minecraft.event.ClickEvent; 27 | import net.minecraft.event.HoverEvent; 28 | import net.minecraft.util.*; 29 | import org.apache.commons.lang3.exception.ExceptionUtils; 30 | 31 | public class ChatUtils { 32 | public static final String COPY_TO_CLIPBOARD_TRANSLATION_KEY = "fancywarpmenu.gui.buttons.copyToClipboard"; 33 | 34 | /** 35 | * Sends a client-side chat message with a warning message and a clickable component used to copy the stacktrace of 36 | * the given throwable. 37 | * 38 | * @param warningMessageTranslationKey translation key of the warning message to be displayed before the clickable component 39 | * @param throwable the throwable to be copied when the prompt is clicked 40 | */ 41 | public static void sendWarningMessageWithCopyableThrowable(String warningMessageTranslationKey, Throwable throwable) { 42 | ChatStyle errorMessageStyle = new ChatStyle().setColor(EnumChatFormatting.GOLD); 43 | sendMessageWithCopyableThrowable(warningMessageTranslationKey, errorMessageStyle, throwable); 44 | } 45 | 46 | /** 47 | * Sends a client-side chat message with an error message and a clickable component used to copy the stacktrace of 48 | * the given throwable. 49 | * 50 | * @param errorMessageTranslationKey translation key of the error message to be displayed before the clickable component 51 | * @param throwable the throwable to be copied when the prompt is clicked 52 | */ 53 | public static void sendErrorMessageWithCopyableThrowable(String errorMessageTranslationKey, Throwable throwable) { 54 | ChatStyle errorMessageStyle = new ChatStyle().setColor(EnumChatFormatting.RED); 55 | sendMessageWithCopyableThrowable(errorMessageTranslationKey, errorMessageStyle, throwable); 56 | } 57 | 58 | /** 59 | * Sends a client-side chat message with the mod name acronym as a prefix. 60 | *
61 | * Example: [FWM] message 62 | * 63 | * @param message the message to send 64 | */ 65 | public static void sendMessageWithModNamePrefix(String message) { 66 | sendMessageWithModNamePrefix(new ChatComponentText(message)); 67 | } 68 | 69 | /** 70 | * Sends a client-side chat message with the mod name acronym as a prefix. 71 | *
72 | * Example: [FWM] message 73 | * 74 | * @param message the message to send 75 | */ 76 | public static void sendMessageWithModNamePrefix(IChatComponent message) { 77 | IChatComponent prefixComponent = createModNamePrefixComponent(); 78 | prefixComponent.appendSibling(message); 79 | Minecraft.getMinecraft().thePlayer.addChatMessage(prefixComponent); 80 | } 81 | 82 | /** 83 | * Returns an {@code IChatComponent} with the acronym of the mod name ("FWM") to be used as a prefix in chat messages 84 | * sent by the mod 85 | * 86 | * @return an {@code IChatComponent} with the acronym of the mod name ("FWM") to be used as a prefix in chat messages 87 | * sent by the mod 88 | */ 89 | private static IChatComponent createModNamePrefixComponent() { 90 | ChatStyle plainStyle = new ChatStyle().setColor(EnumChatFormatting.RESET); 91 | ChatStyle acronymStyle = new ChatStyle() 92 | .setColor(EnumChatFormatting.LIGHT_PURPLE) 93 | .setChatHoverEvent( 94 | new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ChatComponentText("Fancy Warp Menu"))); 95 | 96 | return new ChatComponentText("[") 97 | .appendSibling(new ChatComponentText("FWM").setChatStyle(acronymStyle)) 98 | .appendSibling(new ChatComponentText("] ").setChatStyle(plainStyle)); 99 | } 100 | 101 | /** 102 | * Sends a client-side chat message with a clickable component used to copy the stacktrace of a given throwable. 103 | * 104 | * @param messageTranslationKey translation key of the message to be displayed before the clickable component 105 | * @param messageStyle the {@link ChatStyle} to assign to the {@code IChatComponent} containing the message 106 | * @param throwable the throwable to be copied when the prompt is clicked 107 | */ 108 | private static void sendMessageWithCopyableThrowable(String messageTranslationKey, ChatStyle messageStyle, Throwable throwable) { 109 | if (messageTranslationKey == null) { 110 | throw new NullPointerException("messageTranslationKey cannot be null"); 111 | } else if (messageStyle == null) { 112 | throw new NullPointerException("messageStyle cannot be null"); 113 | } else if (throwable == null) { 114 | throw new NullPointerException("throwable cannot be null"); 115 | } 116 | 117 | ChatStyle plainStyle = new ChatStyle().setColor(EnumChatFormatting.RESET); 118 | // setInsertion gives the component a unique identifier for ca.tirelesstraveler.fancywarpmenu.listeners.ChatListener to look for 119 | ChatStyle copyThrowableStyle = new ChatStyle().setColor(EnumChatFormatting.BLUE) 120 | .setInsertion(messageTranslationKey) 121 | .setChatClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ExceptionUtils.getStackTrace(throwable))); 122 | 123 | IChatComponent chatComponent = createModNamePrefixComponent() 124 | .appendSibling(new ChatComponentTranslation(messageTranslationKey).setChatStyle(messageStyle)) 125 | .appendSibling(new ChatComponentText(" [").setChatStyle(plainStyle)) 126 | .appendSibling(new ChatComponentTranslation(COPY_TO_CLIPBOARD_TRANSLATION_KEY).setChatStyle(copyThrowableStyle)) 127 | .appendSibling(new ChatComponentText("]").setChatStyle(plainStyle)); 128 | Minecraft.getMinecraft().thePlayer.addChatMessage(chatComponent); 129 | } 130 | } -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/GameChecks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.utils; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.FancyWarpMenu; 26 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 27 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.Menu; 28 | import ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.menu.ItemMatchCondition; 29 | import ca.tirelesstraveler.fancywarpmenu.state.GameState; 30 | import net.minecraft.client.Minecraft; 31 | import net.minecraft.inventory.IInventory; 32 | import net.minecraft.scoreboard.Score; 33 | import net.minecraft.scoreboard.Scoreboard; 34 | import org.apache.logging.log4j.LogManager; 35 | import org.apache.logging.log4j.Logger; 36 | 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | import java.util.regex.Matcher; 40 | import java.util.regex.Pattern; 41 | 42 | /** 43 | * This class contains utility methods that determine the values to save in {@link ca.tirelesstraveler.fancywarpmenu.state.GameState} 44 | * from the current state of the SkyBlock server the player is in. 45 | */ 46 | public class GameChecks { 47 | private static final Logger logger = LogManager.getLogger(); 48 | private static final Matcher seasonMatcher = 49 | Pattern.compile("(?Late|Early)? ?(?[a-zA-Z]+) \\d{1,2}.*").matcher(""); 50 | 51 | /** 52 | * Checks the current SkyBlock season and saves using {@link GameState#setSeason(String)}. 53 | */ 54 | public static void checkSeason() { 55 | // Don't run outside of SB to prevent exceptions 56 | if (!Settings.shouldSkipSkyBlockCheck()) { 57 | try { 58 | Scoreboard sb = Minecraft.getMinecraft().theWorld.getScoreboard(); 59 | // SkyBlock sidebar objective 60 | ArrayList scores = (ArrayList) sb.getSortedScores(sb.getObjectiveInDisplaySlot(1)); 61 | 62 | // The date is always near the top (highest score) so we iterate backwards. 63 | for (int i = scores.size(); i > 0; i--) { 64 | Score score = scores.get(i - 1); 65 | String skyBlockScoreboardLine = 66 | sb.getPlayersTeam(score.getPlayerName()).formatString("").trim(); 67 | 68 | seasonMatcher.reset(skyBlockScoreboardLine); 69 | 70 | if (seasonMatcher.matches()) { 71 | String seasonStage = seasonMatcher.group("seasonStage"); 72 | String season = seasonMatcher.group("season"); 73 | 74 | GameState.setSeasonStage(seasonStage); 75 | 76 | if (season != null) { 77 | GameState.setSeason(season); 78 | return; 79 | } 80 | } 81 | } 82 | 83 | GameState.setSeasonStage(null); 84 | GameState.setSeason(null); 85 | } catch (RuntimeException e) { 86 | logger.warn("Failed to check scoreboard season", e); 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * Determines which SkyBlock {@code GuiChest} menu the player is in using the {@link net.minecraft.client.gui.inventory.GuiChest} 93 | * display name. This is used for initial checks when the items haven't loaded in yet. 94 | * 95 | * @param chestInventory the inventory of the chest holding the menu 96 | * @return a {@code Menu} value representing the current menu the player has open 97 | */ 98 | public static Menu determineOpenMenu(IInventory chestInventory) { 99 | if (chestInventory.hasCustomName()) { 100 | String chestTitle = chestInventory.getDisplayName().getUnformattedText(); 101 | 102 | for (Menu menu : FancyWarpMenu.getSkyBlockConstants().getMenuMatchingMap().keySet()) { 103 | if (chestTitle.equals(menu.getMenuDisplayName())) { 104 | return menu; 105 | } 106 | } 107 | } 108 | 109 | return Menu.NONE; 110 | } 111 | 112 | /** 113 | * Determines if the player is in the given menu by checking whether all the {@link ItemMatchCondition}s for that menu 114 | * match the given inventory. This should be used after the inventory has loaded the slot index returned by 115 | * {@link ca.tirelesstraveler.fancywarpmenu.data.skyblockconstants.SkyBlockConstants#getLastMatchConditionInventorySlotIndex(Menu)}. 116 | * If a match is found, the matched menu is saved using {@link GameState#setCurrentMenu(Menu)} 117 | * 118 | * @param menu the {@code Menu} whose match conditions will be checked 119 | * @param chestInventory the inventory to check against the match conditions 120 | * @return {@code true} if all the {@link ItemMatchCondition}s match, {@code false} otherwise 121 | */ 122 | public static boolean menuItemsMatch(Menu menu, IInventory chestInventory) { 123 | List matchConditions = FancyWarpMenu.getSkyBlockConstants().getMenuMatchingMap().get(menu); 124 | 125 | for (ItemMatchCondition matchCondition : matchConditions) { 126 | logger.debug("Starting item match on slot {} for menu {}.", 127 | matchCondition.getInventorySlotIndex(), menu); 128 | 129 | if (!matchCondition.inventoryContainsMatchingItem(chestInventory)) { 130 | logger.warn("Item match on slot {} failed.", matchCondition.getInventorySlotIndex()); 131 | GameState.setCurrentMenu(Menu.NONE); 132 | return false; 133 | } 134 | logger.debug("Finished item match on slot {} for menu {}.", 135 | matchCondition.getInventorySlotIndex(), menu); 136 | } 137 | 138 | GameState.setCurrentMenu(menu); 139 | return true; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/ca/tirelesstraveler/fancywarpmenu/utils/WarpVisibilityChecks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024. TirelessTraveler 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | * OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | package ca.tirelesstraveler.fancywarpmenu.utils; 24 | 25 | import ca.tirelesstraveler.fancywarpmenu.data.Settings; 26 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Island; 27 | import ca.tirelesstraveler.fancywarpmenu.data.layout.Warp; 28 | import ca.tirelesstraveler.fancywarpmenu.state.GameState; 29 | 30 | import java.util.List; 31 | 32 | /** 33 | * Checks used to control the visibility of warps on the fancy warp menu 34 | */ 35 | public class WarpVisibilityChecks { 36 | 37 | /** 38 | * Checks if the given island with a singular warp should be shown on the fancy warp menu. Throws 39 | * {@link IllegalArgumentException} if the island has multiple warps. 40 | * 41 | * @param island the island to check 42 | * @return {@code true} if the island should be visible, {@code false} if it should be hidden 43 | */ 44 | public static boolean shouldShowSingleWarpIsland(Island island) { 45 | if (island.getWarpCount() > 1) { 46 | throw new IllegalArgumentException("Island has more than one warp"); 47 | } 48 | 49 | return shouldShowWarp(island.getWarps().get(0)); 50 | } 51 | 52 | /** 53 | * Checks if a warp should be shown on the fancy warp menu. 54 | * 55 | * @param warp the warp to check 56 | * @return {@code true} if the warp should be visible, {@code false} if it should be hidden 57 | */ 58 | public static boolean shouldShowWarp(Warp warp) { 59 | List warpTags = warp.getTags(); 60 | 61 | if (warpTags != null && !warpTags.isEmpty()) { 62 | for (String tag : warpTags) { 63 | if (!shouldShowWarpWithTag(tag)) { 64 | return false; 65 | } 66 | } 67 | } 68 | 69 | return true; 70 | } 71 | 72 | /** 73 | * Checks if warps with the given tag should be shown on the fancy warp menu 74 | * 75 | * @param tag the categorization tag to check 76 | * @return {@code true} if warps with this tag should be visible, {@code false} if they should be hidden 77 | */ 78 | private static boolean shouldShowWarpWithTag(String tag) { 79 | switch (tag) { 80 | case "bingo": 81 | return !Settings.shouldHideUnobtainableWarps(); 82 | case "jerry": 83 | if (Settings.isDebugModeEnabled() && Settings.shouldAlwaysShowJerryIsland()) { 84 | return true; 85 | } 86 | 87 | if (!Settings.shouldShowJerryIsland()) { 88 | return false; 89 | } 90 | 91 | String season = GameState.getSeason(); 92 | String seasonStage = GameState.getSeasonStage(); 93 | 94 | return season != null && seasonStage != null && season.equals("Winter") && seasonStage.equals("Late"); 95 | default: 96 | return true; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/data/riftLayout.json: -------------------------------------------------------------------------------- 1 | { 2 | "islandList": [ 3 | { 4 | "name": "Rift", 5 | "texturePath": "textures/gui/islands/Rift.png", 6 | "gridX": 21, 7 | "gridY": 6, 8 | "zLevel": 0, 9 | "widthPercentage": 0.35, 10 | "warpList": [ 11 | { 12 | "displayName": "Wizard Tower", 13 | "slotIndex": 10, 14 | "gridX": 18, 15 | "gridY": 25 16 | }, 17 | { 18 | "displayName": "Lagoon Hut", 19 | "slotIndex": 11, 20 | "gridX": 8, 21 | "gridY": 23 22 | }, 23 | { 24 | "displayName": "Dreadfarm", 25 | "slotIndex": 12, 26 | "gridX": 18, 27 | "gridY": 11 28 | }, 29 | { 30 | "displayName": "Plaza", 31 | "slotIndex": 13, 32 | "gridX": 22, 33 | "gridY": 19 34 | }, 35 | { 36 | "displayName": "Colosseum", 37 | "slotIndex": 14, 38 | "gridX": 11, 39 | "gridY": 18 40 | }, 41 | { 42 | "displayName": "Stillgore Château", 43 | "slotIndex": 15, 44 | "gridX": 31, 45 | "gridY": 28 46 | } 47 | ] 48 | } 49 | ], 50 | "warpIcon": { 51 | "texturePath": "textures/gui/Portal.png", 52 | "widthPercentage": 0.02 53 | }, 54 | "configButton": { 55 | "gridX": 60, 56 | "gridY": 31, 57 | "widthPercentage": 0.05 58 | }, 59 | "regularWarpMenuButton": { 60 | "gridX": 60, 61 | "gridY": 29, 62 | "widthPercentage": 0.05 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/data/skyBlockConstants.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "menuMatchingMap": { 3 | "FAST_TRAVEL": [ 4 | { 5 | "inventorySlotIndex": 45, 6 | "itemName": "Island Browser", 7 | "minecraftItemID": "minecraft:blaze_powder" 8 | }, 9 | { 10 | "inventorySlotIndex": 49, 11 | "itemName": "Close", 12 | "minecraftItemID": "minecraft:barrier" 13 | }, 14 | { 15 | "inventorySlotIndex": 53, 16 | "itemName": "Paper Icons", 17 | /* The item ID changes depending on whether the player has paper icons enabled */ 18 | "minecraftItemIDs": [ 19 | "minecraft:map", 20 | "minecraft:filled_map" 21 | ] 22 | } 23 | ], 24 | "PORHTAL": [ 25 | { 26 | "inventorySlotIndex": 31, 27 | "itemName": "Close", 28 | "minecraftItemID": "minecraft:barrier" 29 | } 30 | ] 31 | }, 32 | "warpMessages": { 33 | "warpSuccessMessages": [ 34 | "Warping..." 35 | ], 36 | "warpFailMessages": { 37 | "Unknown destination! Check the Fast Travel menu to view options!": "fancywarpmenu.errors.unknownDestination", 38 | "You haven't unlocked this fast travel destination!": "fancywarpmenu.errors.notUnlocked", 39 | "Couldn't warp you! Try again later. (NO_DESTINATION_FOUND)": "fancywarpmenu.errors.noDestination", 40 | "You need to have visited this island at least once before fast traveling to it!": "fancywarpmenu.errors.notVisited", 41 | "Jerry's Workshop is only available during the Winter!": "fancywarpmenu.errors.notOpenYet" 42 | } 43 | }, 44 | "warpCommandVariants": [ 45 | { 46 | "command": "warp", 47 | "type": "ALIAS" 48 | }, 49 | { 50 | "command": "travel", 51 | "type": "ALIAS" 52 | }, 53 | { 54 | "command": "is", 55 | "type": "WARP" 56 | }, 57 | { 58 | "command": "hub", 59 | "type": "WARP" 60 | }, 61 | { 62 | "command": "warpforge", 63 | "type": "WARP" 64 | }, 65 | { 66 | "command": "savethejerrys", 67 | "type": "WARP" 68 | }, 69 | { 70 | "command": "garry", 71 | "type": "WARP" 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/lang/en_US.lang: -------------------------------------------------------------------------------- 1 | fancywarpmenu.config.categories.general=General 2 | fancywarpmenu.config.categories.general.tooltip=Settings that customize the functionality of the fancy warp menu 3 | fancywarpmenu.config.categories.developerSettings=Developer Settings 4 | fancywarpmenu.config.categories.developerSettings.tooltip=Settings for features used by pack developers to create custom textures and layouts for the fancy warp menu 5 | fancywarpmenu.config.categories.support=Support 6 | fancywarpmenu.config.categories.support.tooltip=Click to join the Fancy Warp Menu Discord server 7 | fancywarpmenu.config.categories.updateAvailable=Update Available 8 | fancywarpmenu.config.categories.updateAvailable.tooltip=There is a new version of Fancy Warp Menu available. Click for more details. 9 | 10 | fancywarpmenu.config.title=Fancy Warp Menu Settings 11 | fancywarpmenu.config.subtitle=Categories 12 | fancywarpmenu.config.warpMenuEnabled=Enable Fancy Warp Menu 13 | fancywarpmenu.config.warpMenuEnabled.tooltip=Replace the default warp menu with the fancy warp menu 14 | fancywarpmenu.config.showIslandLabels=Show Island Names 15 | fancywarpmenu.config.showIslandLabels.tooltip=Show each island's name below its picture 16 | fancywarpmenu.config.hideWarpLabelsUntilIslandHovered=Hide Warp Labels Until Island Hovered 17 | fancywarpmenu.config.hideWarpLabelsUntilIslandHovered.tooltip=Hide the island's warp button labels until the mouse is hovering over the island 18 | fancywarpmenu.config.hideWarpLabelForIslandsWithOneWarp=Hide Warp Label for One Warp Islands 19 | fancywarpmenu.config.hideWarpLabelForIslandsWithOneWarp.tooltip=Hide warp label for islands with only one warp 20 | fancywarpmenu.config.suggestWarpMenuOnWarpCommand=Reminders to Use Fancy Warp Menu 21 | fancywarpmenu.config.suggestWarpMenuOnWarpCommand.tooltip=Show reminders to use the fancy warp menu when the player uses a warp command 22 | fancywarpmenu.config.addWarpCommandToChatHistory=Add Warp Command to Chat History 23 | fancywarpmenu.config.addWarpCommandToChatHistory.tooltip=Add warp commands sent by the fancy warp menu to your chat history 24 | fancywarpmenu.config.showJerryIsland=Show Jerry's Workshop 25 | fancywarpmenu.config.showJerryIsland.tooltip=Show the Jerry's Workshop island on the fancy warp menu 26 | fancywarpmenu.config.hideUnobtainableWarps=Hide Unobtainable Warps 27 | fancywarpmenu.config.hideUnobtainableWarps.tooltip=Hide warps only obtainable during special game modes such as Bingo 28 | fancywarpmenu.config.enableUpdateNotification=Enable Update Notification 29 | fancywarpmenu.config.enableUpdateNotification.tooltip=Show a notification on the settings button and in the settings menu when a new version of Fancy Warp Menu is available 30 | fancywarpmenu.config.showRegularWarpMenuButton=Show Regular Warp Menu Button 31 | fancywarpmenu.config.showRegularWarpMenuButton.tooltip=Show the button to access the regular warp menu 32 | 33 | fancywarpmenu.config.developerModeEnabled=Enable Developer Mode 34 | fancywarpmenu.config.developerModeEnabled.tooltip=Enable all developer features 35 | fancywarpmenu.config.showDebugOverlay=Show Debug Overlay 36 | fancywarpmenu.config.showDebugOverlay.tooltip=Show an overlay with coordinate values and button borders over the fancy warp menu. Press TAB while the warp menu is open to toggle. 37 | fancywarpmenu.config.drawBorders=Draw Button Borders 38 | fancywarpmenu.config.drawBorders.tooltip=Show borders around island and warp buttons. Press B while the fancy warp menu is open to toggle. 39 | fancywarpmenu.config.skipSkyBlockCheck=Skip SkyBlock Check 40 | fancywarpmenu.config.skipSkyBlockCheck.tooltip=Skip checking for whether the player is on SkyBlock 41 | fancywarpmenu.config.alwaysShowJerryIsland=Always Show Jerry's Workshop 42 | fancywarpmenu.config.alwaysShowJerryIsland.tooltip=Always show Jerry's Workshop on the warp menu regardless of the value of the general > showJerryIsland setting and the in-game season 43 | 44 | fancywarpmenu.gui.buttons.config=Config 45 | fancywarpmenu.gui.buttons.copyToClipboard=Copy to Clipboard 46 | fancywarpmenu.gui.buttons.copyToClipboard.copied=Copied! 47 | fancywarpmenu.gui.buttons.regularWarpMenu=Regular Warp Menu 48 | 49 | fancywarpmenu.key.openMenu=Open the Fancy Warp Menu 50 | 51 | fancywarpmenu.key.categories.fancyWarpMenu=Fancy Warp Menu 52 | 53 | fancywarpmenu.messages.useWarpMenuInsteadOfCommand=Reminder to use the Fancy Warp Menu instead! 54 | fancywarpmenu.messages.fancyWarpMenuEnabled=Fancy Warp Menu Enabled 55 | fancywarpmenu.messages.fancyWarpMenuDisabled=Fancy Warp Menu Disabled 56 | 57 | fancywarpmenu.errors.notUnlocked=Not Unlocked 58 | fancywarpmenu.errors.unknownDestination=Unknown Destination 59 | fancywarpmenu.errors.noDestination=No Destination Found 60 | fancywarpmenu.errors.notVisited=Visit Location to Unlock Warp 61 | fancywarpmenu.errors.notOpenYet=Not Open Yet 62 | fancywarpmenu.errors.slotNumberOutOfBounds=Invalid Slot Number (%d) 63 | fancywarpmenu.errors.slotHasNoItem=Slot %d Has No Item to Click On 64 | fancywarpmenu.errors.mouseIsHoldingItem=Mouse is Already Holding an Item 65 | fancywarpmenu.errors.fancyWarpGui.initFailed=%s Initialization Failed 66 | fancywarpmenu.errors.fancyWarpGui.chestInventoryTooSmall=Chest inventory size (%d) is smaller than expected (%d), falling back to vanilla chest UI 67 | fancywarpmenu.errors.fancyWarpGui.itemMatchFailed=Chest item match check failed, falling back to vanilla chest UI -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/Logo.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/Notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/Notification.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/Portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/Portal.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/Regular Warp Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/Regular Warp Menu.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Barn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Barn.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Crimson Isle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Crimson Isle.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Deep Caverns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Deep Caverns.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Dungeon Hub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Dungeon Hub.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/End.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/End.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Garden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Garden.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Gold Mine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Gold Mine.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Hub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Hub.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Jerry's Workshop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Jerry's Workshop.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Mushroom Desert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Mushroom Desert.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Park.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Park.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Private Island.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Private Island.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Rift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Rift.png -------------------------------------------------------------------------------- /src/main/resources/assets/fancywarpmenu/textures/gui/islands/Spider's Den.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ILikePlayingGames/FancyWarpMenu/a8aab6c29521b954e042ed6ff44da2c32c7d12f8/src/main/resources/assets/fancywarpmenu/textures/gui/islands/Spider's Den.png -------------------------------------------------------------------------------- /src/main/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "fancywarpmenu", 4 | "name": "Fancy Warp Menu", 5 | "description": "A recreation of the fancy warp menu from SkyblockAddons\\nSupport: https://discord.gg/tXFf9umfA9\\nLicenses: https://github.com/ILikePlayingGames/FancyWarpMenu/wiki/Licenses", 6 | "version": "${version}", 7 | "mcversion": "${mcversion}", 8 | "url": "https://github.com/ILikePlayingGames/FancyWarpMenu", 9 | "authorList": [ 10 | "TirelessTraveler" 11 | ], 12 | "credits": "Nea89, SBA contributors, Schlaumeyer, Fandom SkyBlock Wiki", 13 | "logoFile": "assets/fancywarpmenu/textures/gui/Logo.png", 14 | "screenshots": [], 15 | "dependencies": [] 16 | } 17 | 18 | ] -------------------------------------------------------------------------------- /src/main/resources/version.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2024. TirelessTraveler 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | # OR OTHER DEALINGS IN THE SOFTWARE. 21 | # 22 | 23 | # Forge expects a mod version in the main mod class or in this file, otherwise it logs a warning on startup. 24 | # suppress inspection "UnusedProperty" 25 | fancywarpmenu.version = ${version} -------------------------------------------------------------------------------- /version/README.md: -------------------------------------------------------------------------------- 1 | # update.json 2 | This is a Forge Update Checker file, whose format is specified [here](https://docs.minecraftforge.net/en/1.20.x/misc/updatechecker/). 3 | The mod will fetch it on Minecraft startup to check if a new version is available. -------------------------------------------------------------------------------- /version/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://github.com/ILikePlayingGames/FancyWarpMenu/releases", 3 | "promos": { 4 | "1.8.9-recommended": "1.0", 5 | "1.8.9-latest": "2.0-beta.1" 6 | }, 7 | "1.8.9": { 8 | "1.0": "New textures and additional language support", 9 | "0.9.0+77": "Link update available button to GitHub releases" 10 | } 11 | } --------------------------------------------------------------------------------