├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── gradle.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── changelogs ├── 1.6.0.md ├── 1.6.1.md ├── 1.6.2.md ├── 1.7.0.md ├── 1.7.1.md ├── 2.0.0.md ├── 2.0.1.md ├── 2.0.2.md ├── 2.0.3.md ├── 2.10.0.md ├── 2.11.0.md ├── 2.11.1.md ├── 2.11.2.md ├── 2.12.0.md ├── 2.13.1.md ├── 2.13.2.md ├── 2.13.3.md ├── 2.13.4.md ├── 2.13.5.md ├── 2.14.0.md ├── 2.2.0.md ├── 2.3.0.md ├── 2.4.0.md ├── 2.4.1.md ├── 2.4.2.md ├── 2.5.0.md ├── 2.6.0.md ├── 2.7.0.md ├── 2.8.0.md ├── 2.9.0.md ├── 2.9.1.md ├── 2.9.2.md ├── 2.9.3.md └── 2.9.4.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── affect-hand-fov-disabled-example.gif ├── cinematic-cam-example.gif ├── individual-transition.png ├── keybind-conflict-detection.png ├── relative-sensitivity-example.gif ├── relative-view-bobbing-example.gif ├── scroll-zoom-example.gif ├── secondary-zoom.gif ├── spyglass-integration-example.gif ├── zoom-example.gif ├── zoom-hand.webp └── zoom-world.webp ├── settings.gradle.kts ├── src └── main │ ├── java │ └── dev │ │ └── isxander │ │ └── zoomify │ │ └── mixins │ │ ├── hooks │ │ └── MinecraftClientMixin.java │ │ ├── spyglass │ │ ├── AbstractClientPlayerEntityMixin.java │ │ ├── InGameHudMixin.java │ │ └── SpyglassItemMixin.java │ │ └── zoom │ │ ├── GameRendererMixin.java │ │ ├── MouseMixin.java │ │ └── secondary │ │ └── GameRendererMixin.java │ ├── kotlin │ └── dev │ │ └── isxander │ │ └── zoomify │ │ ├── Zoomify.kt │ │ ├── config │ │ ├── Presets.kt │ │ ├── SettingsGuiFactory.kt │ │ ├── SpyglassBehaviour.kt │ │ ├── ZoomKeyBehaviour.kt │ │ ├── ZoomifySettings.kt │ │ ├── demo │ │ │ ├── ControlEmulation.kt │ │ │ └── ZoomDemoImageRenderer.kt │ │ └── migrator │ │ │ ├── Migration.kt │ │ │ ├── MigrationAvailableScreen.kt │ │ │ ├── MigrationResultScreen.kt │ │ │ ├── Migrator.kt │ │ │ └── impl │ │ │ └── OkZoomerMigrator.kt │ │ ├── integrations │ │ ├── ControlifyIntegration.kt │ │ ├── IntegrationHelper.kt │ │ └── ModMenuIntegration.kt │ │ ├── utils │ │ ├── KeySource.kt │ │ ├── MinecraftExt.kt │ │ ├── NameableEnumKt.kt │ │ └── TransitionType.kt │ │ └── zoom │ │ ├── DefaultZoomHelpers.kt │ │ ├── Interpolator.kt │ │ └── ZoomHelper.kt │ └── resources │ ├── assets │ └── zoomify │ │ ├── lang │ │ ├── de_de.json │ │ ├── en_gb.json │ │ ├── en_us.json │ │ ├── es_es.json │ │ ├── es_mx.json │ │ ├── et_ee.json │ │ ├── fr_fr.json │ │ ├── ja_jp.json │ │ ├── pt_br.json │ │ ├── ru_ru.json │ │ ├── sv_se.json │ │ ├── tr_tr.json │ │ ├── vi_vn.json │ │ ├── zh_cn.json │ │ └── zh_tw.json │ │ ├── textures │ │ └── demo │ │ │ ├── third-person-hud.webp │ │ │ ├── third-person-view.webp │ │ │ ├── zoom-hand.webp │ │ │ └── zoom-world.webp │ │ └── zoomify.png │ ├── fabric.mod.json │ └── zoomify.mixins.json ├── stonecutter.gradle.kts └── versions ├── 1.20.1 └── gradle.properties ├── 1.20.4 └── gradle.properties ├── 1.20.6 └── gradle.properties ├── 1.21.1 └── gradle.properties └── 1.21.3 └── gradle.properties /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: isxander 2 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Gradle CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - 'README.md' 9 | - 'LICENSE' 10 | - '.gitignore' 11 | - '.editorconfig' 12 | - 'changelogs/**' 13 | pull_request: 14 | branches: 15 | - '**' 16 | paths-ignore: 17 | - 'README.md' 18 | - 'LICENSE' 19 | - '.gitignore' 20 | - '.editorconfig' 21 | - 'changelogs/**' 22 | - 'src/*/resources/lang/*' 23 | workflow_dispatch: 24 | jobs: 25 | build: 26 | runs-on: ubuntu-latest 27 | name: Build with gradle 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Set up JDK 32 | uses: actions/setup-java@v4 33 | with: 34 | java-version: 21 35 | distribution: temurin 36 | - name: Grant execute permission for gradlew 37 | run: chmod +x gradlew 38 | - name: Build with Gradle 39 | uses: Wandalen/wretry.action@master 40 | with: 41 | command: ./gradlew buildAllVersions --stacktrace 42 | - uses: actions/upload-artifact@v4 43 | with: 44 | path: versions/**/build/libs/*.jar 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | run 2 | build 3 | /.idea 4 | .gradle 5 | !.gitkeep 6 | .kotlin 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![logo](https://dl.isxander.dev/logos/zoomify/v3/zoomify-128x.png) 4 | 5 | # Zoomify 6 | 7 | [![Mod Loader](https://img.shields.io/badge/Mod%20Loader-Fabric-lightyellow?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTEyLTE2VDE2OjU0OjE3LTA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHN0RXZ0OndoZW49IjIwMTgtMTItMTZUMTY6NTQ6MTctMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4/HiGMAAAAtUlEQVRYw+XXrQqAMBQF4D2P2eBL+QIG8RnEJFaNBjEum+0+zMQLtwwv+wV3ZzhhMDgfJ0wUSinxZUQWgKos1JP/AbD4OneIDyQPwCFniA+EJ4CaXm4TxAXCC0BNHgLhAdAnx9hC8PwGSRtAFVMQjF7cNTWED8B1cgwW20yfJgAvrssAsZ1cB3g/xckAxr6FmCDU5N6f488BrpCQ4rQBJkiMYh4ACmLzwOQF0CExinkCsvw7vgGikl+OotaKRwAAAABJRU5ErkJggg==)](https://fabricmc.net) 8 | ![Enviroment](https://img.shields.io/badge/Enviroment-Client-purple) 9 | [![Discord](https://img.shields.io/discord/780023008668287017?color=blue&logo=discord&label=Discord)](https://short.isxander.dev/discord) 10 | 11 | [![Modrinth](https://img.shields.io/modrinth/dt/zoomify?color=00AF5C&label=downloads&logo=modrinth)](https://modrinth.com/mod/zoomify) 12 | [![CurseForge](https://cf.way2muchnoise.eu/full_zoomify_downloads.svg)](https://curseforge.com/minecraft/mc-mods/zoomify) 13 | 14 | [![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/isxander) 15 | 16 | *A zoom mod with infinite customizability.* 17 | 18 | Zoomify aims to provide the easiest, most configurable and most sexy looking zoom mod you will ever find. 19 | 20 | ![Zoom Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/zoom-example.gif) 21 | 22 | [![](https://www.bisecthosting.com/partners/custom-banners/08bbd3ff-5c0d-4480-8738-de0f070a04dd.png)](https://bisecthosting.com/xander) 23 | 24 |
25 | 26 | ## How to use 27 | ### Keybind 28 | Initially, Minecraft has a keybind that overrides Zoomify by default. 29 | Go to the controls menu and make sure the Zoomify keybind (default `C`) 30 | isn't conflicting (goes red). 31 | 32 | ### Open settings menu 33 | You can access the settings menu with two ways. 34 | 35 | - The client command `/zoomify` 36 | - [Mod Menu](https://modrinth.com/mod/modmenu) settings button 37 | 38 | ## Features 39 | ### Scroll Zoom 40 | You can zoom in further using your scroll wheel. 41 | 42 | ![Scroll Zoom Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/scroll-zoom-example.gif) 43 | 44 | ### Spyglass Integration 45 | You can configure Zoomify to only be able to zoom in when you are holding or carrying a spyglass. 46 | Or even just override the spyglass zoom with Zoomify! 47 | 48 | Show the spyglass overlay and play spyglass sound effects when using Zoomify. 49 | 50 | ![Spyglass Integration Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/spyglass-integration-example.gif) 51 | 52 | ### Transitions 53 | **14 different** transitions to choose from! 54 | 55 | - Instant 56 | - Linear 57 | - [Ease in Sine](https://easings.net/#easeInSine) 58 | - [Ease out Sine](https://easings.net/#easeOutSine) 59 | - [Ease in/out Sine](https://easings.net/#easeInOutSine) 60 | - [Ease in Quad](https://easings.net/#easeInQuad) 61 | - [Ease out Quad](https://easings.net/#easeOutQuad) 62 | - [Ease in/out Quad](https://easings.net/#easeInOutQuad) 63 | - [Ease in Cubic](https://easings.net/#easeInCubic) 64 | - [Ease out Cubic](https://easings.net/#easeOutCubic) 65 | - [Ease in/out Cubic](https://easings.net/#easeInOutCubic) 66 | - [Ease in Exponential](https://easings.net/#easeInExp) 67 | - [Ease out Exponential](https://easings.net/#easeOutExp) 68 | - [Ease in/out Exponential](https://easings.net/#easeInOutExp) 69 | 70 | You can even pick what transition you would like for zooming in and out individually! 71 | 72 | ![Individual Transition Config](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/individual-transition.png) 73 | 74 | ### Relative Sensitivity 75 | Reduce your mouse sensitivity based on the amount of zoom, 76 | so you have fine control over your player. 77 | 78 | ![Relative Sensitivity Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/relative-sensitivity-example.gif) 79 | 80 | ### Relative View Bobbing 81 | Reduce the view bobbing effect based on the amount of zoom, 82 | so you can walk and zoom at the same time! 83 | 84 | ![Relative View Bobbing Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/relative-view-bobbing-example.gif) 85 | 86 | ### Cinematic Camera 87 | Make the mouse/camera smooth, like the zoom in [OptiFine](https://www.optifine.net). 88 | 89 | ![Cinematic Cam Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/cinematic-cam-example.gif) 90 | 91 | ### Affect Hand FOV 92 | Turn off this setting to prevent your hand from also being zoomed in! 93 | 94 | ![Affect Hand FOV Turned Off Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/affect-hand-fov-disabled-example.gif) 95 | 96 | ### Secondary Zoom 97 | 98 | Completely separate zoom from normal Zoomify. 99 | Aimed for content creators, features `Hide HUD While Zooming` option and longer zoom in times. 100 | 101 | ![Secondary Zoom Example](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/secondary-zoom.gif) 102 | 103 | ### Presets 104 | 105 | You can reset to default, make Zoomify behave like OptiFine and others! 106 | 107 | ### Smart Keybinding Detection 108 | 109 | On first launch, if a keybinding is conflicting with Zoomify's zoom, a notification will appear notifying them of this. 110 | 111 | ![Keybinding Conflict Detection](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/keybind-conflict-detection.png) 112 | 113 | ### Highly configurable 114 | Absolutely **NO** hard-coded values. Everything about your zoom is configurable! 115 | 116 | ### Control the zoom speed with seconds 117 | Instead of some stupid arbitrary value such as zoom speed, you can pick 118 | the exact amount of seconds it will take to zoom in, with a separate setting for zooming out. 119 | 120 | ### Credits 121 | - [Xander](https://github.com/isXander) - Developer 122 | - [MoonTidez](https://github.com/MoonTidez) - Logo designer 123 | 124 | #### Translators 125 | Languages in alphabetical order 126 | 127 | - [神枪968](https://github.com/GodGun968) - 简体中文 / Chinese (Simplified) 128 | - [Xander](https://github.com/isXander) - English (United Kingdom) 129 | - [Madis0](https://github.com/Madis0) - eesti keel / Estonian 130 | - [co-91](https://github.com/co-91) - 日本語 / Japanese 131 | - [Rodrigo Appelt](https://github.com/Agentew04) - Português / Portuguese 132 | - [SyberiaK](https://github.com/SyberiaK) - Русский / Russian 133 | - [Felix14-v2](https://github.com/Felix14-v2) - Русский / Russian 134 | - [Zetsphiron](https://github.com/Zetsphiron) - Español (mexicano) / Spanish (Mexico) 135 | - [Zetsphiron](https://github.com/Zetsphiron) - Español (españa) / Spanish (Spain) 136 | - [localfossa](https://github.com/localfossa) - Türkçe / Turkish 137 | - [Im Vietnam](https://github.com/ImVietnam) - Tiếng Việt / Vietnamese 138 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | 4 | val kotlinVersion = "2.1.10" 5 | kotlin("jvm") version kotlinVersion 6 | kotlin("plugin.serialization") version kotlinVersion 7 | 8 | id("fabric-loom") version "1.10.+" 9 | 10 | id("me.modmuss50.mod-publish-plugin") version "0.8.4" 11 | `maven-publish` 12 | 13 | id("org.ajoberstar.grgit") version "5.0.+" 14 | } 15 | 16 | val mcVersion = property("mcVersion")!!.toString() 17 | val mcSemverVersion = stonecutter.current.version 18 | val mcDep = property("fmj.mcDep").toString() 19 | 20 | group = "dev.isxander" 21 | val versionWithoutMC = "2.14.3" 22 | version = "$versionWithoutMC+${stonecutter.current.project}" 23 | 24 | val isAlpha = "alpha" in version.toString() 25 | val isBeta = "beta" in version.toString() 26 | 27 | base { 28 | archivesName.set(property("modName").toString()) 29 | } 30 | 31 | loom { 32 | if (stonecutter.current.isActive) { 33 | runConfigs.all { 34 | ideConfigGenerated(true) 35 | runDir("../../run") 36 | } 37 | } 38 | 39 | mixin { 40 | useLegacyMixinAp.set(false) 41 | } 42 | } 43 | 44 | stonecutter { 45 | swap( 46 | "fov-precision", 47 | if (stonecutter.eval(stonecutter.current.version, ">=1.21.2")) 48 | "float" else "double" 49 | ) 50 | } 51 | 52 | repositories { 53 | mavenCentral() 54 | maven("https://maven.terraformersmc.com") 55 | maven("https://maven.isxander.dev/releases") 56 | maven("https://maven.isxander.dev/snapshots") 57 | maven("https://maven.parchmentmc.org") 58 | maven("https://maven.quiltmc.org/repository/release") 59 | exclusiveContent { 60 | forRepository { maven("https://api.modrinth.com/maven") } 61 | filter { includeGroup("maven.modrinth") } 62 | } 63 | exclusiveContent { 64 | forRepository { maven("https://cursemaven.com") } 65 | filter { includeGroup("curse.maven") } 66 | } 67 | maven("https://jitpack.io") 68 | maven("https://maven.flashyreese.me/snapshots") 69 | maven("https://oss.sonatype.org/content/repositories/snapshots") 70 | } 71 | 72 | dependencies { 73 | minecraft("com.mojang:minecraft:$mcVersion") 74 | mappings(loom.layered { 75 | // quilt does not support pre-releases so it is necessary to only layer if they exist 76 | optionalProp("deps.parchment") { 77 | parchment("org.parchmentmc.data:parchment-$it@zip") 78 | } 79 | 80 | officialMojangMappings() 81 | }) 82 | modImplementation("net.fabricmc:fabric-loader:${property("deps.fabricLoader")}") 83 | 84 | val fapiVersion = property("deps.fabricApi").toString() 85 | listOf( 86 | "fabric-resource-loader-v0", 87 | "fabric-lifecycle-events-v1", 88 | "fabric-key-binding-api-v1", 89 | "fabric-command-api-v2", 90 | ).forEach { 91 | modImplementation(fabricApi.module(it, fapiVersion)) 92 | } 93 | modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:$fapiVersion") // so you can do `depends: fabric-api` in FMJ 94 | modImplementation("net.fabricmc:fabric-language-kotlin:${property("deps.flk")}") 95 | 96 | modApi("dev.isxander:yet-another-config-lib:${property("deps.yacl")}") { 97 | // was including old fapi version that broke things at runtime 98 | exclude(group = "net.fabricmc.fabric-api", module = "fabric-api") 99 | } 100 | 101 | // mod menu compat 102 | optionalProp("deps.modMenu") { 103 | modImplementation("com.terraformersmc:modmenu:$it") 104 | } 105 | 106 | optionalProp("deps.controlify") { 107 | modImplementation("dev.isxander:controlify:$it") 108 | } 109 | 110 | modImplementation(include("com.akuleshov7:ktoml-core-jvm:${property("deps.ktoml")}")!!) 111 | } 112 | 113 | tasks { 114 | processResources { 115 | val modId: String by project 116 | val modName: String by project 117 | val modDescription: String by project 118 | val githubProject: String by project 119 | 120 | val props = mapOf( 121 | "id" to modId, 122 | "group" to project.group, 123 | "name" to modName, 124 | "description" to modDescription, 125 | "version" to project.version, 126 | "github" to githubProject, 127 | "mc" to mcDep 128 | ) 129 | 130 | props.forEach(inputs::property) 131 | 132 | filesMatching("fabric.mod.json") { 133 | expand(props) 134 | } 135 | 136 | eachFile { 137 | // don't include photoshop files for the textures for development 138 | if (name.endsWith(".psd")) { 139 | exclude() 140 | } 141 | } 142 | } 143 | 144 | register("releaseMod") { 145 | group = "mod" 146 | 147 | dependsOn("publishMods") 148 | dependsOn("publish") 149 | } 150 | } 151 | 152 | val javaMajorVersion = property("java.version").toString().toInt() 153 | java { 154 | withSourcesJar() 155 | 156 | javaMajorVersion 157 | .let { JavaVersion.values()[it - 1] } 158 | .let { 159 | sourceCompatibility = it 160 | targetCompatibility = it 161 | } 162 | } 163 | 164 | tasks.withType { 165 | options.release = javaMajorVersion 166 | } 167 | kotlin { 168 | jvmToolchain(javaMajorVersion) 169 | } 170 | 171 | publishMods { 172 | displayName.set("Zoomify $versionWithoutMC for MC $mcVersion") 173 | file.set(tasks.remapJar.get().archiveFile) 174 | changelog.set( 175 | rootProject.file("changelogs/${versionWithoutMC}.md") 176 | .takeIf { it.exists() } 177 | ?.readText() 178 | ?: "No changelog provided." 179 | ) 180 | type.set(when { 181 | isAlpha -> ALPHA 182 | isBeta -> BETA 183 | else -> STABLE 184 | }) 185 | modLoaders.add("fabric") 186 | 187 | fun versionList(prop: String) = findProperty(prop)?.toString() 188 | ?.split(',') 189 | ?.map { it.trim() } 190 | ?: emptyList() 191 | 192 | // modrinth and curseforge use different formats for snapshots. this can be expressed globally 193 | val stableMCVersions = versionList("pub.stableMC") 194 | 195 | val modrinthId: String by project 196 | if (modrinthId.isNotBlank() && hasProperty("modrinth.token")) { 197 | modrinth { 198 | projectId.set(modrinthId) 199 | accessToken.set(findProperty("modrinth.token")?.toString()) 200 | minecraftVersions.addAll(stableMCVersions) 201 | minecraftVersions.addAll(versionList("pub.modrinthMC")) 202 | 203 | requires { slug.set("fabric-api") } 204 | requires { slug.set("yacl") } 205 | optional { slug.set("modmenu") } 206 | } 207 | } 208 | 209 | val curseforgeId: String by project 210 | if (curseforgeId.isNotBlank() && hasProperty("curseforge.token")) { 211 | curseforge { 212 | projectId.set(curseforgeId) 213 | accessToken.set(findProperty("curseforge.token")?.toString()) 214 | minecraftVersions.addAll(stableMCVersions) 215 | minecraftVersions.addAll(versionList("pub.curseMC")) 216 | 217 | requires { slug.set("fabric-api") } 218 | requires { slug.set("yacl") } 219 | optional { slug.set("modmenu") } 220 | } 221 | } 222 | 223 | val githubProject: String by project 224 | if (githubProject.isNotBlank() && hasProperty("github.token")) { 225 | github { 226 | repository.set(githubProject) 227 | accessToken.set(findProperty("github.token")?.toString()) 228 | commitish.set(grgit.branch.current().name) 229 | } 230 | } 231 | } 232 | 233 | publishing { 234 | publications { 235 | create("mod") { 236 | groupId = "dev.isxander" 237 | artifactId = "zoomify" 238 | 239 | from(components["java"]) 240 | } 241 | } 242 | 243 | repositories { 244 | val username = "XANDER_MAVEN_USER".let { System.getenv(it) ?: findProperty(it) }?.toString() 245 | val password = "XANDER_MAVEN_PASS".let { System.getenv(it) ?: findProperty(it) }?.toString() 246 | if (username != null && password != null) { 247 | maven(url = "https://maven.isxander.dev/releases") { 248 | name = "XanderReleases" 249 | credentials { 250 | this.username = username 251 | this.password = password 252 | } 253 | } 254 | } else { 255 | println("Xander Maven credentials not satisfied.") 256 | } 257 | } 258 | } 259 | 260 | fun optionalProp(property: String, block: (String) -> T?) { 261 | findProperty(property)?.toString()?.takeUnless { it.isBlank() }?.let(block) 262 | } 263 | 264 | fun isPropDefined(property: String): Boolean { 265 | return findProperty(property)?.toString()?.isNotBlank() ?: false 266 | } 267 | -------------------------------------------------------------------------------- /changelogs/1.6.0.md: -------------------------------------------------------------------------------- 1 | - Fix zoom speed being FPS dependent 2 | - Remove telemetry and update checking 3 | - Allow up to 150% zoom speed 4 | -------------------------------------------------------------------------------- /changelogs/1.6.1.md: -------------------------------------------------------------------------------- 1 | - 1.19 release 2 | - Fix animation being jumpy when you stop zooming before the animation is completed 3 | - Increase max scroll zoom 4 | - Add more scroll zoom steps 5 | - Internally switch to Settxi enums to now the multi-choice options are toggles rather than dropdowns 6 | -------------------------------------------------------------------------------- /changelogs/1.6.2.md: -------------------------------------------------------------------------------- 1 | - Fix crash 2 | -------------------------------------------------------------------------------- /changelogs/1.7.0.md: -------------------------------------------------------------------------------- 1 | - Relative Sensitivity Gradient - Controls the gradient of the sensitivity based on the zoom level. 2 | -------------------------------------------------------------------------------- /changelogs/1.7.1.md: -------------------------------------------------------------------------------- 1 | - Removed scroll zoom transitions in favour of "Smooth Transition" on/off option. 2 | - Doubled amount of scroll steps from 10 to 20 3 | -------------------------------------------------------------------------------- /changelogs/2.0.0.md: -------------------------------------------------------------------------------- 1 | ## Highlights 2 | 3 | ### Spyglass Integration 4 | 5 | - `Zoom Behaviour` option. Controls if Zoomify can only be used 6 | with a spyglass or override the vanilla spyglass zoom. 7 | - `Overlay Visibility` option controls when to render the 8 | spyglass overlay when zooming with Zoomify. 9 | - `Sound Behaviour` option controls when to play the 10 | zoom in and out sounds of the spyglass. 11 | 12 | ### Relative View Bobbing 13 | 14 | - Added `Relative View Bobbing` option which reduced the amount of bobbing 15 | depending on the zoom level to make walking less jarring. 16 | 17 | ### Zoom Speed Changes 18 | 19 | - Remove `Zoom Speed` option in favour of `Zoom In Time` and `Zoom Out Time` 20 | which dictates amount of seconds it takes to do said action. 21 | - Remove `Scroll Zoom Speed` option in favour of `Scroll Zoom Smoothness`. 22 | The higher the smoothness the longer and smoother the scrolling is. 23 | 24 | ## Full Patch Notes 25 | 26 | - `Zoom Behaviour` option. Controls if Zoomify can only be used 27 | with a spyglass or override the vanilla spyglass zoom. 28 | - `Overlay Visibility` option controls when to render the 29 | spyglass overlay when zooming with Zoomify. 30 | - `Sound Behaviour` option controls when to play the 31 | zoom in and out sounds of the spyglass. 32 | - Remove `Zoom Speed` option in favour of `Zoom In Time` and `Zoom Out Time` 33 | which dictates amount of seconds it takes to do said action. 34 | - Added `Relative View Bobbing` option which reduced the amount of bobbing 35 | depending on the zoom level to make walking less jarring. 36 | - Remove `Scroll Zoom Speed` option in favour of `Scroll Zoom Smoothness`. 37 | The higher the smoothness the longer and smoother the scrolling is. 38 | - Remove `Relative Sensitivity Gradient` to reduce down to just the one 39 | `Relative Sensitivity` setting which can be disabled by setting the slider to 0. 40 | - When letting go of the zoom key or toggling zoom off whilst scrolled in, 41 | the normal zoom out transition is used with the same amount of time as if you weren't scrolled. 42 | - Increased number of scroll steps to 30. 43 | - Apply function to scroll zoom steps to make the first few steps smaller 44 | and the last steps larger to create a more linear effect. 45 | -------------------------------------------------------------------------------- /changelogs/2.0.1.md: -------------------------------------------------------------------------------- 1 | - Fix zoom in/out time being halved in-game 2 | - Add Russian translation 3 | - Update Estonian translation 4 | - Fix typo in English (US & GB) 5 | -------------------------------------------------------------------------------- /changelogs/2.0.2.md: -------------------------------------------------------------------------------- 1 | - Remove settings keybind. 2 | - Add `/zoomify` client command. 3 | -------------------------------------------------------------------------------- /changelogs/2.0.3.md: -------------------------------------------------------------------------------- 1 | - Fix Ease Out Cubic transition 2 | -------------------------------------------------------------------------------- /changelogs/2.10.0.md: -------------------------------------------------------------------------------- 1 | - Updated to 1.20 (still supports 1.19.4) 2 | - Updated to YACL 3.x 3 | -------------------------------------------------------------------------------- /changelogs/2.11.0.md: -------------------------------------------------------------------------------- 1 | # Zoomify 2.11.0 2 | 3 | ## New Features 4 | 5 | Added a live zoom preview to most of the Zoomify settings. 6 | 7 | This now allows you to see the effect of your changes without saving, 8 | leaving the menu and entering a world. This should make it easier to configure 9 | all the settings to your liking. 10 | 11 | ## Language Updates 12 | 13 | - Added Vietnamese by [Im Vietnam](https://github.com/ImVietnam) ([#160](https://github.com/isXander/Zoomify/pull/160)). 14 | - Added Japanese by [co-91](https://github.com/co-91) ([#167](https://github.com/isXander/Zoomify/pull/167)). 15 | -------------------------------------------------------------------------------- /changelogs/2.11.1.md: -------------------------------------------------------------------------------- 1 | - Update for YACL 3.1.1+1.20 2 | -------------------------------------------------------------------------------- /changelogs/2.11.2.md: -------------------------------------------------------------------------------- 1 | - Fix broken Controlify compatibility 2 | -------------------------------------------------------------------------------- /changelogs/2.12.0.md: -------------------------------------------------------------------------------- 1 | # Zoomify 2.12.0 for 1.20.2 2 | 3 | - Update to 1.20.2 4 | -------------------------------------------------------------------------------- /changelogs/2.13.1.md: -------------------------------------------------------------------------------- 1 | - Update to support Controlify 2.0 2 | -------------------------------------------------------------------------------- /changelogs/2.13.2.md: -------------------------------------------------------------------------------- 1 | - *Actually* fix Controlify on 2.x.x 2 | -------------------------------------------------------------------------------- /changelogs/2.13.3.md: -------------------------------------------------------------------------------- 1 | - Update to 1.20.5 2 | -------------------------------------------------------------------------------- /changelogs/2.13.4.md: -------------------------------------------------------------------------------- 1 | # Zoomify 2.13.4 2 | 3 | Implemented the Stonecutter build system to target multiple versions, 4 | like Controlify and YACL! 5 | 6 | This comes with the benefit that Zoomify will now receive updates for the older versions, 7 | so the Controlify 2.0 incompatibility is now solved on 1.20.1 8 | 9 | ## Targets 10 | 11 | Zoomify now targets the following Minecraft versions: 12 | - 1.20.1 13 | - 1.20.4 (also supports 1.20.3) 14 | - 1.20.6 (also supports 1.20.5) 15 | Make sure you download the correct version for your Minecraft version! 16 | 17 | ## Translations 18 | 19 | - Updated Mexican Spanish translation (thanks to @TheLegendofSaram) 20 | -------------------------------------------------------------------------------- /changelogs/2.13.5.md: -------------------------------------------------------------------------------- 1 | # Zoomify 2.13.5 2 | 3 | Zoomify now targets the following Minecraft versions: 4 | 5 | - 1.20.1 6 | - 1.20.4 (also supports 1.20.3) 7 | - 1.20.6 (also supports 1.20.5) 8 | 9 | Make sure you download the correct version for your Minecraft version! 10 | 11 | ## Changes 12 | 13 | - Fix Controlify compatibility 14 | - Remove zoom key behaviour option and instead have two separate keybinds 15 | -------------------------------------------------------------------------------- /changelogs/2.14.0.md: -------------------------------------------------------------------------------- 1 | # Zoomify 2.14.0 2 | 3 | Zoomify now targets the following Minecraft versions: 4 | 5 | - 1.20.1 6 | - 1.20.4 (also supports 1.20.3) 7 | - 1.20.6 (also supports 1.20.5) 8 | - 1.21 9 | 10 | Make sure you download the correct version for your Minecraft version! 11 | 12 | ## Changes 13 | 14 | - Presets now have demo animations in their descriptions 15 | - 1.21 support 16 | - Internally rewrite settings with new experimental YetAnotherConfigLib API 17 | -------------------------------------------------------------------------------- /changelogs/2.2.0.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 3 | - You can now independently change the zoom in and out transitions. 4 | 5 | ![](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/independent-zoom-transition.png) 6 | 7 | ## Translation Updates 8 | 9 | - Add Spanish (Spain) Translation - [Zetsphiron](https://github.com/isXander/Zoomify/pull/73) 10 | - Add Spanish (Mexico) Translation - [Zetsphiron](https://github.com/isXander/Zoomify/pull/73) 11 | - Update Chinese (Simplified) Translation - [神枪968](https://github.com/isXander/Zoomify/pull/67) 12 | - Update Portuguese Translation - [Rodrigo Appelt](https://github.com/isXander/Zoomify/pull/69) 13 | -------------------------------------------------------------------------------- /changelogs/2.3.0.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 3 | - You can now apply presets! As of now there are 3 to choose from: 4 | + Default - Resets all settings to default 5 | + OptiFine - Makes Zoomify behave like OptiFine zoom 6 | + OK Zoomer - Makes Zoomify behave like OK Zoomer defaults (useful for migrating) 7 | 8 | ![presets page](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/presets-page.png) 9 | 10 | - Retain scroll steps setting: remember the amount of times you scrolled in since last zoom 11 | - Affect hand fov: on by default, turning off doesn't make your hand zoom in 12 | - Linear-like zoom setting: make scroll zoom increments appear more linear 13 | + This was already in Zoomify, but it wasn't able to be turned off. 14 | 15 | 16 | -------------------------------------------------------------------------------- /changelogs/2.4.0.md: -------------------------------------------------------------------------------- 1 | ## Keybinding Conflict Detection 2 | - Upon first launch of Zoomify (detected when config file doesn't exist), a toast appears explaining that Zoomify 3 | won't work, and you need to go to settings and unbind conflicting keybindings. 4 | 5 | ![](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/keybind-conflict-detection.png) 6 | 7 | - New button in settings: Unbind conflicting keys does just that, with a toast for each key that was modified. 8 | 9 | ![](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/unbound-conflicting-key-toast.png) 10 | 11 | - `Presets` category renamed to `Misc` and presets moved into subcategory 12 | 13 | ![](https://raw.githubusercontent.com/isXander/Zoomify/1.19/screenshots/misc-page.png) 14 | -------------------------------------------------------------------------------- /changelogs/2.4.1.md: -------------------------------------------------------------------------------- 1 | - Fix conflict detection 2 | -------------------------------------------------------------------------------- /changelogs/2.4.2.md: -------------------------------------------------------------------------------- 1 | - Add `Support me!` button to config 2 | - Update Estonian translations 3 | -------------------------------------------------------------------------------- /changelogs/2.5.0.md: -------------------------------------------------------------------------------- 1 | - Converted Cinematic Camera to a slider, 0% for off, 100% for normal etc 2 | - Add suffixes to some sliders, for example: 3 | * Initial Zoom: 4x 4 | * Relative Sensitivity: 100% 5 | * Cinematic Camera: 0% 6 | - Update default zoom times: 7 | * Zoom In: 1.0 secs 8 | * Zoom Out: 0.5 secs 9 | -------------------------------------------------------------------------------- /changelogs/2.6.0.md: -------------------------------------------------------------------------------- 1 | - Switched to [YetAnotherConfigLib](https://curseforge.com/minecraft/mc-mods/yacl) (you need to download it) library rather than Cloth Config 2 | - Decrease maximum zoom in and out time to 5 seconds for better slider usability. 3 | - Update translations. 4 | -------------------------------------------------------------------------------- /changelogs/2.7.0.md: -------------------------------------------------------------------------------- 1 | - Keybind Scrolling: Instead of using scroll wheel, use keybinds! (Configurable) 2 | - Fix funky behaviour with Spyglass integration 3 | -------------------------------------------------------------------------------- /changelogs/2.8.0.md: -------------------------------------------------------------------------------- 1 | ## Secondary Zoom 2 | This new feature is a completely separate zoom to normal Zoomify. 3 | Its primary goal is to compete with a similar feature in 4 | [Camera Utils](https://curseforge.com/minecraft/mc-mods/camera-utils) (with more settings of course) 5 | that I have seen many content creators love. 6 | 7 | All you need to do is press another keybinding found in options! (F6 by default) 8 | 9 | This secondary zoom has a few exclusive features but misses advanced ones present in 10 | regular zoom that aren't really needed for the intended use case. These include: 11 | 12 | - Option to hide HUD whilst zooming 13 | - Cinematic camera is always on 14 | - An increased maximum zoom in time (5secs -> 30secs) 15 | - Toggleable only 16 | - No transitions - linear only 17 | - No additional mouse settings 18 | 19 | This feature came with a semi-major rewrite to the internals of Zoomify, 20 | though you shouldn't notice a difference. If you do, make sure to report it on the 21 | [issue tracker](https://github.com/isXander/Zoomify/issues). 22 | -------------------------------------------------------------------------------- /changelogs/2.9.0.md: -------------------------------------------------------------------------------- 1 | - Build on 1.19.3 (though 1.19.2 is still supported) 2 | - Add automatic migration from OK Zoomer to Zoomify 3 | -------------------------------------------------------------------------------- /changelogs/2.9.1.md: -------------------------------------------------------------------------------- 1 | - Re-compile with latest version of YetAnotherConfigLib as new version JAR isn't backwards compatible 2 | -------------------------------------------------------------------------------- /changelogs/2.9.2.md: -------------------------------------------------------------------------------- 1 | - Fix crash when checking migrations 2 | -------------------------------------------------------------------------------- /changelogs/2.9.3.md: -------------------------------------------------------------------------------- 1 | - Fix instant transition not being instant 2 | -------------------------------------------------------------------------------- /changelogs/2.9.4.md: -------------------------------------------------------------------------------- 1 | - Updated chinese translation 2 | - New german translation 3 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2G 2 | 3 | kotlin.code.style=official 4 | systemProp.kotlinVersion=2.1.10 5 | 6 | modId=zoomify 7 | modName=Zoomify 8 | modDescription=A zoom mod with infinite customizability. 9 | 10 | modrinthId=w7ThoJFB 11 | curseforgeId=574741 12 | githubProject=isXander/Zoomify 13 | 14 | deps.fabricLoader=0.16.10 15 | deps.ktoml=0.5.2 16 | deps.flk=1.13.1+kotlin.2.1.10 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/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.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original 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 POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /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% equ 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% equ 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 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /screenshots/affect-hand-fov-disabled-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/affect-hand-fov-disabled-example.gif -------------------------------------------------------------------------------- /screenshots/cinematic-cam-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/cinematic-cam-example.gif -------------------------------------------------------------------------------- /screenshots/individual-transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/individual-transition.png -------------------------------------------------------------------------------- /screenshots/keybind-conflict-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/keybind-conflict-detection.png -------------------------------------------------------------------------------- /screenshots/relative-sensitivity-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/relative-sensitivity-example.gif -------------------------------------------------------------------------------- /screenshots/relative-view-bobbing-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/relative-view-bobbing-example.gif -------------------------------------------------------------------------------- /screenshots/scroll-zoom-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/scroll-zoom-example.gif -------------------------------------------------------------------------------- /screenshots/secondary-zoom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/secondary-zoom.gif -------------------------------------------------------------------------------- /screenshots/spyglass-integration-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/spyglass-integration-example.gif -------------------------------------------------------------------------------- /screenshots/zoom-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/zoom-example.gif -------------------------------------------------------------------------------- /screenshots/zoom-hand.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/zoom-hand.webp -------------------------------------------------------------------------------- /screenshots/zoom-world.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/screenshots/zoom-world.webp -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | import dev.kikugie.stonecutter.StonecutterSettings 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenCentral() 6 | gradlePluginPortal() 7 | maven("https://maven.fabricmc.net") 8 | maven("https://maven.quiltmc.org/repository/release") 9 | maven("https://maven.kikugie.dev/releases") 10 | } 11 | } 12 | 13 | plugins { 14 | id("dev.kikugie.stonecutter") version "0.4.5" 15 | } 16 | 17 | extensions.configure { 18 | kotlinController = true 19 | centralScript = "build.gradle.kts" 20 | shared { 21 | versions("1.20.1", "1.20.4", "1.20.6", "1.21.1", "1.21.3") 22 | } 23 | create(rootProject) 24 | } 25 | 26 | rootProject.name = "Zoomify" 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/hooks/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.hooks; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import dev.isxander.zoomify.Zoomify; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.server.packs.resources.ReloadInstance; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(Minecraft.class) 11 | public class MinecraftClientMixin { 12 | @ModifyExpressionValue( 13 | method = "", 14 | at = @At( 15 | value = "INVOKE", 16 | target = "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;createReload(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/server/packs/resources/ReloadInstance;" 17 | ) 18 | ) 19 | private ReloadInstance onReloadResources(ReloadInstance resourceReload) { 20 | resourceReload.done().thenRun(Zoomify.INSTANCE::onGameFinishedLoading); 21 | return resourceReload; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/spyglass/AbstractClientPlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.spyglass; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import dev.isxander.zoomify.config.SpyglassBehaviour; 5 | import dev.isxander.zoomify.config.ZoomifySettings; 6 | import net.minecraft.client.player.AbstractClientPlayer; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(AbstractClientPlayer.class) 11 | public class AbstractClientPlayerEntityMixin { 12 | @ModifyExpressionValue( 13 | method = "getFieldOfViewModifier", 14 | at = @At(value = "CONSTANT", args = "floatValue=0.1f") 15 | ) 16 | private float modifySpyglassFovMultiplier(float multiplier) { 17 | if (ZoomifySettings.Companion.getSpyglassBehaviour().get() != SpyglassBehaviour.COMBINE) 18 | return 1f; 19 | return multiplier; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/spyglass/InGameHudMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.spyglass; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import dev.isxander.zoomify.Zoomify; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.Gui; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | 12 | @Mixin(Gui.class) 13 | public class InGameHudMixin { 14 | @Shadow @Final private Minecraft minecraft; 15 | 16 | @ModifyExpressionValue( 17 | /*? if >1.20.4 {*/ 18 | method = "renderCameraOverlays", 19 | /*?} else {*/ 20 | /*method = "render", 21 | *//*?}*/ 22 | at = @At( 23 | value = "INVOKE", 24 | target = "Lnet/minecraft/client/player/LocalPlayer;isScoping()Z" 25 | ) 26 | ) 27 | private boolean shouldRenderSpyglassOverlay(boolean isUsingSpyglass) { 28 | return Zoomify.shouldRenderOverlay(minecraft.player, isUsingSpyglass); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/spyglass/SpyglassItemMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.spyglass; 2 | 3 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 4 | import dev.isxander.zoomify.config.OverlayVisibility; 5 | import dev.isxander.zoomify.config.SoundBehaviour; 6 | import dev.isxander.zoomify.config.ZoomifySettings; 7 | import net.minecraft.sounds.SoundEvent; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.item.SpyglassItem; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | 13 | @Mixin(SpyglassItem.class) 14 | public class SpyglassItemMixin { 15 | @WrapWithCondition( 16 | method = {"use", "stopUsing"}, 17 | at = @At( 18 | value = "INVOKE", 19 | target = "Lnet/minecraft/world/entity/player/Player;playSound(Lnet/minecraft/sounds/SoundEvent;FF)V" 20 | ) 21 | ) 22 | private boolean shouldPlaySpyglassSound(Player instance, SoundEvent event, float volume, float pitch) { 23 | if (ZoomifySettings.Companion.getSpyglassSoundBehaviour().get() == SoundBehaviour.NEVER) 24 | return false; 25 | 26 | if (ZoomifySettings.Companion.getSpyglassSoundBehaviour().get() == SoundBehaviour.WITH_OVERLAY 27 | && ZoomifySettings.Companion.getSpyglassOverlayVisibility().get() == OverlayVisibility.NEVER) { 28 | return false; 29 | } 30 | 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/zoom/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.zoom; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 5 | import com.llamalad7.mixinextras.sugar.Local; 6 | import dev.isxander.zoomify.Zoomify; 7 | import dev.isxander.zoomify.config.ZoomifySettings; 8 | import net.minecraft.client.Camera; 9 | import net.minecraft.client.renderer.GameRenderer; 10 | import net.minecraft.util.Mth; 11 | import org.objectweb.asm.Opcodes; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | 15 | @Mixin(GameRenderer.class) 16 | public class GameRendererMixin { 17 | @ModifyReturnValue( 18 | method = "getFov", 19 | at = @At("RETURN") 20 | ) 21 | private /*$ fov-precision >>*/ float modifyFovWithZoom( 22 | /*$ fov-precision >>*/ float fov, 23 | @Local(argsOnly = true) float tickDelta 24 | ) { 25 | return fov / Zoomify.getZoomDivisor(tickDelta); 26 | } 27 | 28 | @ModifyExpressionValue( 29 | method = "bobView", 30 | at = { 31 | @At( 32 | value = "FIELD", 33 | //? if >=1.21.2 { 34 | target = "Lnet/minecraft/client/player/AbstractClientPlayer;oBob:F", 35 | //?} else { 36 | /*target = "Lnet/minecraft/world/entity/player/Player;oBob:F", 37 | *///?} 38 | opcode = Opcodes.GETFIELD 39 | ), 40 | @At( 41 | value = "FIELD", 42 | //? if >=1.21.2 { 43 | target = "Lnet/minecraft/client/player/AbstractClientPlayer;bob:F", 44 | //?} else { 45 | /*target = "Lnet/minecraft/world/entity/player/Player;bob:F", 46 | *///?} 47 | opcode = Opcodes.GETFIELD 48 | ), 49 | } 50 | ) 51 | private float modifyBobbingIntensity(float p) { 52 | if (!ZoomifySettings.Companion.getRelativeViewBobbing().get()) 53 | return p; 54 | 55 | return (float) (p / Mth.lerp(0.2, 1.0, Zoomify.INSTANCE.getPreviousZoomDivisor())); 56 | } 57 | 58 | @ModifyExpressionValue( 59 | method = "renderItemInHand", 60 | at = @At( 61 | value = "INVOKE", 62 | //? if >=1.21.2 { 63 | target = "Lnet/minecraft/client/renderer/GameRenderer;getFov(Lnet/minecraft/client/Camera;FZ)F" 64 | //?} else { 65 | /*target = "Lnet/minecraft/client/renderer/GameRenderer;getFov(Lnet/minecraft/client/Camera;FZ)D" 66 | *///?} 67 | ) 68 | ) 69 | private /*$ fov-precision >>*/ float keepHandFov( 70 | /*$ fov-precision >>*/ float fov, 71 | @Local(argsOnly=true) float tickDelta 72 | ) { 73 | if (!ZoomifySettings.Companion.getAffectHandFov().get()) 74 | return fov * Zoomify.getZoomDivisor(tickDelta); 75 | return fov; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/zoom/MouseMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.zoom; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.llamalad7.mixinextras.sugar.Local; 5 | import dev.isxander.zoomify.Zoomify; 6 | import dev.isxander.zoomify.config.SpyglassBehaviour; 7 | import dev.isxander.zoomify.config.ZoomifySettings; 8 | import net.minecraft.client.MouseHandler; 9 | import net.minecraft.util.Mth; 10 | import org.joml.Vector2i; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.ModifyArg; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | @Mixin(MouseHandler.class) 19 | public class MouseMixin { 20 | 21 | /*? if <=1.20.1 {*/ 22 | /*@Shadow 23 | private double accumulatedScroll; 24 | *//*?}*/ 25 | 26 | @Inject( 27 | method = "onScroll", 28 | /*? if >1.20.1 {*/ 29 | at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSpectator()Z"), 30 | /*?} else {*/ 31 | /*at = @At(value = "FIELD", target = "Lnet/minecraft/client/MouseHandler;accumulatedScroll:D", ordinal = 7), 32 | *//*?}*/ 33 | cancellable = true 34 | ) 35 | private void scrollStepCounter( 36 | CallbackInfo ci 37 | //? if >=1.21.2 { 38 | , @Local Vector2i scroll 39 | //?} elif >1.20.1 { 40 | /*, @Local(ordinal = 1) int scrollY 41 | *///?} 42 | ) { 43 | //? if >=1.21.2 { 44 | int scrollY = scroll.y; 45 | /*?} elif <=1.20.1 {*/ 46 | /*double scrollY = accumulatedScroll; 47 | *//*?}*/ 48 | 49 | if (ZoomifySettings.Companion.getScrollZoom().get() 50 | && Zoomify.INSTANCE.getZooming() && scrollY != 0 51 | && !ZoomifySettings.Companion.getKeybindScrolling()) { 52 | Zoomify.mouseZoom(scrollY); 53 | ci.cancel(); 54 | } 55 | } 56 | 57 | @ModifyExpressionValue( 58 | method = "turnPlayer", 59 | at = @At(value = "FIELD", target = "Lnet/minecraft/client/Options;smoothCamera:Z") 60 | ) 61 | private boolean smoothCameraIfZoom(boolean original) { 62 | return original 63 | || Zoomify.INSTANCE.getSecondaryZooming() 64 | || (Zoomify.INSTANCE.getZooming() && ZoomifySettings.Companion.getCinematicCamera().get() > 0); 65 | } 66 | 67 | @ModifyExpressionValue( 68 | method = "turnPlayer", 69 | at = @At( 70 | value = "INVOKE", 71 | target = "Lnet/minecraft/client/OptionInstance;get()Ljava/lang/Object;", 72 | ordinal = 0 73 | ) 74 | ) 75 | private Object applyRelativeSensitivity(Object genericValue) { 76 | double value = (Double) genericValue; 77 | return value / Mth.lerp( 78 | ZoomifySettings.Companion.getRelativeSensitivity().get() / 100.0, 79 | 1.0, 80 | Zoomify.INSTANCE.getPreviousZoomDivisor() 81 | ); 82 | } 83 | 84 | @ModifyExpressionValue( 85 | method = "turnPlayer", 86 | at = @At( 87 | value = "INVOKE", 88 | target = "Lnet/minecraft/client/player/LocalPlayer;isScoping()Z" 89 | ) 90 | ) 91 | private boolean shouldApplySpyglassSensitivity(boolean isUsingSpyglass) { 92 | if (ZoomifySettings.Companion.getSpyglassBehaviour().get() != SpyglassBehaviour.COMBINE) 93 | return false; 94 | return isUsingSpyglass; 95 | } 96 | 97 | @ModifyArg( 98 | method = "turnPlayer", 99 | at = @At( 100 | value = "INVOKE", 101 | target = "Lnet/minecraft/util/SmoothDouble;getNewDeltaValue(DD)D" 102 | ), 103 | index = 1 104 | ) 105 | private double modifyCinematicSmoothness(double smoother) { 106 | if (Zoomify.INSTANCE.getZooming() && ZoomifySettings.Companion.getCinematicCamera().get() > 0) 107 | return smoother / (ZoomifySettings.Companion.getCinematicCamera().get() / 100.0); 108 | 109 | return smoother; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/dev/isxander/zoomify/mixins/zoom/secondary/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.mixins.zoom.secondary; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import dev.isxander.zoomify.Zoomify; 5 | import dev.isxander.zoomify.config.ZoomifySettings; 6 | import net.minecraft.client.renderer.GameRenderer; 7 | import org.objectweb.asm.Opcodes; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | 11 | @Mixin(GameRenderer.class) 12 | public class GameRendererMixin { 13 | @ModifyExpressionValue( 14 | method = "render", 15 | at = @At( 16 | value = "FIELD", 17 | target = "Lnet/minecraft/client/Options;hideGui:Z", 18 | opcode = Opcodes.GETFIELD 19 | ) 20 | ) 21 | private boolean shouldHideHUD(boolean hideHUD) { 22 | return hideHUD || (Zoomify.INSTANCE.getSecondaryZooming() && ZoomifySettings.Companion.getSecondaryHideHUDOnZoom().get()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/Zoomify.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify 2 | 3 | import com.mojang.blaze3d.platform.InputConstants 4 | import dev.isxander.yacl3.config.v3.value 5 | import dev.isxander.zoomify.config.* 6 | import dev.isxander.zoomify.config.migrator.Migrator 7 | import dev.isxander.zoomify.integrations.constrainModVersionIfLoaded 8 | import dev.isxander.zoomify.utils.toast 9 | import dev.isxander.zoomify.zoom.* 10 | import net.fabricmc.api.ClientModInitializer 11 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal 12 | import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback 13 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents 14 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper 15 | import net.minecraft.client.KeyMapping 16 | import net.minecraft.client.Minecraft 17 | import net.minecraft.client.player.AbstractClientPlayer 18 | import net.minecraft.network.chat.Component 19 | import net.minecraft.sounds.SoundEvents 20 | import net.minecraft.util.Mth 21 | import net.minecraft.world.item.ItemStack 22 | import net.minecraft.world.item.Items 23 | import org.slf4j.LoggerFactory 24 | 25 | object Zoomify : ClientModInitializer { 26 | val LOGGER = LoggerFactory.getLogger("Zoomify")!! 27 | 28 | private val zoomKey = KeyMapping("zoomify.key.zoom", InputConstants.Type.KEYSYM, InputConstants.KEY_C, "zoomify.key.category") 29 | private val secondaryZoomKey = KeyMapping("zoomify.key.zoom.secondary", InputConstants.Type.KEYSYM, InputConstants.KEY_F6, "zoomify.key.category") 30 | private val scrollZoomIn = KeyMapping("zoomify.key.zoom.in", -1, "zoomify.key.category") 31 | private val scrollZoomOut = KeyMapping("zoomify.key.zoom.out", -1, "zoomify.key.category") 32 | 33 | var zooming = false 34 | private set 35 | private val zoomHelper = RegularZoomHelper(ZoomifySettings) 36 | 37 | var secondaryZooming = false 38 | private set 39 | private val secondaryZoomHelper = SecondaryZoomHelper(ZoomifySettings) 40 | 41 | var previousZoomDivisor = 1.0 42 | private set 43 | 44 | const val maxScrollTiers = 30 45 | private var scrollSteps = 0 46 | 47 | private var shouldPlaySound = false 48 | 49 | private var displayGui = false 50 | 51 | override fun onInitializeClient() { 52 | // controlify compat only works on 2.x 53 | constrainModVersionIfLoaded("controlify", "2.x.x") 54 | 55 | // imports on 56 | ZoomifySettings 57 | 58 | KeyBindingHelper.registerKeyBinding(zoomKey) 59 | KeyBindingHelper.registerKeyBinding(secondaryZoomKey) 60 | if (ZoomifySettings.keybindScrolling) { 61 | KeyBindingHelper.registerKeyBinding(scrollZoomIn) 62 | KeyBindingHelper.registerKeyBinding(scrollZoomOut) 63 | } 64 | 65 | ClientCommandRegistrationCallback.EVENT.register { dispatcher, _ -> 66 | dispatcher.register( 67 | literal("zoomify").executes { 68 | displayGui = true 69 | 0 70 | } 71 | ) 72 | } 73 | 74 | ClientTickEvents.END_CLIENT_TICK.register(this::tick) 75 | } 76 | 77 | private fun tick(minecraft: Minecraft) { 78 | val prevZooming = zooming 79 | 80 | when (ZoomifySettings.zoomKeyBehaviour.value) { 81 | ZoomKeyBehaviour.HOLD -> zooming = zoomKey.isDown 82 | ZoomKeyBehaviour.TOGGLE -> { 83 | while (zoomKey.consumeClick()) { 84 | zooming = !zooming 85 | } 86 | } 87 | } 88 | 89 | while (secondaryZoomKey.consumeClick()) { 90 | secondaryZooming = !secondaryZooming 91 | } 92 | 93 | if (ZoomifySettings.keybindScrolling) { 94 | while (scrollZoomIn.consumeClick()) { 95 | scrollSteps++ 96 | } 97 | while (scrollZoomOut.consumeClick()) { 98 | scrollSteps-- 99 | } 100 | 101 | scrollSteps = scrollSteps.coerceIn(0..maxScrollTiers) 102 | } 103 | 104 | handleSpyglass(minecraft, prevZooming) 105 | 106 | zoomHelper.tick(zooming, scrollSteps) 107 | secondaryZoomHelper.tick(secondaryZooming, scrollSteps) 108 | 109 | if (displayGui) { 110 | displayGui = false 111 | minecraft.setScreen(createSettingsGui(minecraft.screen)) 112 | } 113 | } 114 | 115 | @JvmStatic 116 | fun getZoomDivisor(tickDelta: Float): Float { 117 | if (!zooming) { 118 | if (!ZoomifySettings.retainZoomSteps.value) 119 | scrollSteps = 0 120 | 121 | zoomHelper.reset() 122 | } 123 | 124 | return (zoomHelper.getZoomDivisor(tickDelta).also { previousZoomDivisor = it } * secondaryZoomHelper.getZoomDivisor(tickDelta)).toFloat() 125 | } 126 | 127 | @JvmStatic 128 | fun mouseZoom(mouseDelta: Double) { 129 | if (mouseDelta > 0) { 130 | scrollSteps++ 131 | } else if (mouseDelta < 0) { 132 | scrollSteps-- 133 | } 134 | scrollSteps = scrollSteps.coerceIn(0..maxScrollTiers) 135 | } 136 | 137 | private fun handleSpyglass(minecraft: Minecraft, prevZooming: Boolean) { 138 | val cameraEntity = minecraft.cameraEntity 139 | 140 | if (cameraEntity is AbstractClientPlayer) { 141 | when (ZoomifySettings.spyglassBehaviour.value) { 142 | SpyglassBehaviour.ONLY_ZOOM_WHILE_HOLDING -> { 143 | if (!cameraEntity.isHolding(Items.SPYGLASS)) 144 | zooming = false 145 | } 146 | SpyglassBehaviour.ONLY_ZOOM_WHILE_CARRYING -> 147 | if (!cameraEntity.inventory.hasAnyMatching { it.`is`(Items.SPYGLASS) }) 148 | zooming = false 149 | SpyglassBehaviour.OVERRIDE -> 150 | if (cameraEntity.isScoping) 151 | zooming = zooming && minecraft.options.cameraType.isFirstPerson 152 | else -> {} 153 | } 154 | 155 | val requiresSpyglass = ZoomifySettings.spyglassBehaviour.value != SpyglassBehaviour.COMBINE 156 | if (requiresSpyglass && cameraEntity.isScoping) { 157 | zooming = true 158 | } 159 | 160 | if (shouldPlaySound) { 161 | if (!zooming && prevZooming) { 162 | cameraEntity.playSound(SoundEvents.SPYGLASS_STOP_USING, 1f, 1f) 163 | } 164 | } 165 | 166 | shouldPlaySound = when (ZoomifySettings.spyglassSoundBehaviour.value) { 167 | SoundBehaviour.NEVER -> false 168 | SoundBehaviour.ALWAYS -> true 169 | SoundBehaviour.ONLY_SPYGLASS -> cameraEntity.isScoping || (requiresSpyglass && zooming && cameraEntity.isHolding(Items.SPYGLASS)) 170 | SoundBehaviour.WITH_OVERLAY -> shouldRenderOverlay( 171 | cameraEntity, 172 | minecraft.options.cameraType.isFirstPerson && cameraEntity.isScoping 173 | ) && requiresSpyglass 174 | } 175 | 176 | if (shouldPlaySound) { 177 | if (zooming && !prevZooming) { 178 | cameraEntity.playSound(SoundEvents.SPYGLASS_USE, 1f, 1f) 179 | } 180 | } 181 | } 182 | } 183 | 184 | @JvmStatic 185 | fun shouldRenderOverlay(player: AbstractClientPlayer, isUsingSpyglass: Boolean) = 186 | when (ZoomifySettings.spyglassOverlayVisibility.value) { 187 | OverlayVisibility.NEVER -> false 188 | OverlayVisibility.ALWAYS -> zooming 189 | OverlayVisibility.HOLDING -> isUsingSpyglass 190 | || (zooming && player.isHolding(Items.SPYGLASS)) 191 | && ZoomifySettings.spyglassBehaviour.value != SpyglassBehaviour.COMBINE 192 | OverlayVisibility.CARRYING -> zooming 193 | && player.inventory.hasAnyMatching { stack: ItemStack -> stack.`is`(Items.SPYGLASS) } 194 | } 195 | 196 | fun onGameFinishedLoading() { 197 | if (ZoomifySettings.firstLaunch) { 198 | LOGGER.info("Zoomify detected first launch!") 199 | detectConflictingToast() 200 | 201 | Migrator.checkMigrations() 202 | } 203 | } 204 | 205 | fun unbindConflicting(): Boolean { 206 | val minecraft = Minecraft.getInstance() 207 | if (!zoomKey.isUnbound) { 208 | for (key in minecraft.options.keyMappings) { 209 | if (key != zoomKey && key.equals(zoomKey)) { 210 | key.setKey(InputConstants.UNKNOWN) 211 | minecraft.options.save() 212 | 213 | toast( 214 | Component.translatable("zoomify.toast.unbindConflicting.name"), 215 | Component.translatable("zoomify.toast.unbindConflicting.description", 216 | Component.translatable(key.name) 217 | ), 218 | longer = false 219 | ) 220 | 221 | return true 222 | } 223 | } 224 | } 225 | 226 | return false 227 | } 228 | 229 | private fun detectConflictingToast() { 230 | val minecraft = Minecraft.getInstance() 231 | 232 | if (zoomKey.isUnbound) 233 | return 234 | 235 | if (minecraft.options.keyMappings.any { it != zoomKey && it.equals(zoomKey) }) { 236 | toast( 237 | Component.translatable("zoomify.toast.conflictingKeybind.title"), 238 | Component.translatable("zoomify.toast.conflictingKeybind.description", 239 | Component.translatable("yacl3.config.zoomify.category.misc") 240 | ), 241 | longer = true 242 | ) 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/Presets.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config 2 | 3 | import dev.isxander.yacl3.config.v3.ConfigEntry 4 | import dev.isxander.yacl3.config.v3.default 5 | import dev.isxander.yacl3.config.v3.value 6 | import dev.isxander.zoomify.utils.TransitionType 7 | import net.minecraft.network.chat.Component 8 | 9 | enum class Presets(val displayName: Component, val apply: ZoomifySettings.() -> Unit) { 10 | Default("zoomify.gui.preset.default", { 11 | this.allSettings.forEach { it.setToDefault() } 12 | }), 13 | Optifine("zoomify.gui.preset.optifine", { 14 | Default.apply(this) 15 | 16 | this.zoomInTransition.value = TransitionType.INSTANT 17 | this.zoomOutTransition.value = TransitionType.INSTANT 18 | this.scrollZoom.value = false 19 | this.relativeSensitivity.value = 0 20 | this.relativeViewBobbing.value = false 21 | this.cinematicCamera.value = 100 22 | }), 23 | OkZoomer("zoomify.gui.preset.ok_zoomer", { 24 | Default.apply(this) 25 | 26 | this.zoomInTime.value = 0.25 27 | this.zoomOutTime.value = 0.25 28 | this.relativeSensitivity.value = 50 29 | this.relativeViewBobbing.value = false 30 | this.scrollZoomSmoothness.value = 25 31 | this.linearLikeSteps.value = false 32 | }); 33 | 34 | constructor(displayName: String, apply: ZoomifySettings.() -> Unit) 35 | : this(Component.translatable(displayName), apply) 36 | } 37 | 38 | private fun ConfigEntry.setToDefault() = set(default) 39 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/SpyglassBehaviour.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config 2 | 3 | import dev.isxander.zoomify.utils.NameableEnumKt 4 | import net.minecraft.network.chat.Component 5 | import net.minecraft.util.StringRepresentable 6 | 7 | enum class SpyglassBehaviour(override val localisedName: Component) : NameableEnumKt, StringRepresentable { 8 | COMBINE("zoomify.spyglass_behaviour.combine"), 9 | OVERRIDE("zoomify.spyglass_behaviour.override"), 10 | ONLY_ZOOM_WHILE_HOLDING("zoomify.spyglass_behaviour.only_zoom_while_holding"), 11 | ONLY_ZOOM_WHILE_CARRYING("zoomify.spyglass_behaviour.only_zoom_while_carrying"); 12 | 13 | override fun getSerializedName(): String = name.lowercase() 14 | 15 | constructor(localisedName: String) : this(Component.translatable(localisedName)) 16 | 17 | companion object { 18 | val CODEC = StringRepresentable.fromEnum(::values) 19 | } 20 | } 21 | 22 | enum class OverlayVisibility(override val localisedName: Component) : NameableEnumKt, StringRepresentable { 23 | NEVER("zoomify.overlay_visibility.never"), 24 | HOLDING("zoomify.overlay_visibility.holding"), 25 | CARRYING("zoomify.overlay_visibility.carrying"), 26 | ALWAYS("zoomify.overlay_visibility.always"); 27 | 28 | override fun getSerializedName(): String = name.lowercase() 29 | 30 | constructor(localisedName: String) : this(Component.translatable(localisedName)) 31 | 32 | companion object { 33 | val CODEC = StringRepresentable.fromEnum(::values) 34 | } 35 | } 36 | 37 | enum class SoundBehaviour(override val localisedName: Component) : NameableEnumKt, StringRepresentable { 38 | NEVER("zoomify.sound_behaviour.never"), 39 | ALWAYS("zoomify.sound_behaviour.always"), 40 | ONLY_SPYGLASS("zoomify.sound_behaviour.only_spyglass"), 41 | WITH_OVERLAY("zoomify.sound_behaviour.with_overlay"); 42 | 43 | override fun getSerializedName(): String = name.lowercase() 44 | 45 | constructor(localisedName: String) : this(Component.translatable(localisedName)) 46 | 47 | companion object { 48 | val CODEC = StringRepresentable.fromEnum(::values) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/ZoomKeyBehaviour.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config 2 | 3 | import dev.isxander.zoomify.utils.NameableEnumKt 4 | import net.minecraft.network.chat.Component 5 | import net.minecraft.util.StringRepresentable 6 | 7 | enum class ZoomKeyBehaviour(override val localisedName: Component) : NameableEnumKt, StringRepresentable { 8 | HOLD("zoomify.zoom_key_behaviour.hold"), 9 | TOGGLE("zoomify.zoom_key_behaviour.toggle"); 10 | 11 | override fun getSerializedName(): String = name.lowercase() 12 | 13 | constructor(localisedName: String) : this(Component.translatable(localisedName)) 14 | 15 | companion object { 16 | val CODEC = StringRepresentable.fromEnum(::values) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/ZoomifySettings.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config 2 | 3 | import com.mojang.serialization.Codec 4 | import dev.isxander.yacl3.config.v3.JsonFileCodecConfig 5 | import dev.isxander.yacl3.config.v3.register 6 | import dev.isxander.yacl3.config.v3.value 7 | import dev.isxander.zoomify.utils.TransitionType 8 | import net.fabricmc.loader.api.FabricLoader 9 | 10 | open class ZoomifySettings() : JsonFileCodecConfig( 11 | FabricLoader.getInstance().configDir.resolve("zoomify.json") 12 | ) { 13 | val initialZoom by register(default = 4, Codec.INT) 14 | 15 | val zoomInTime by register(default = 1.0, Codec.DOUBLE) 16 | val zoomOutTime by register(default = 0.5, Codec.DOUBLE) 17 | 18 | val zoomInTransition by register(default = TransitionType.EASE_OUT_EXP, TransitionType.CODEC) 19 | val zoomOutTransition by register(default = TransitionType.EASE_OUT_EXP, TransitionType.CODEC) 20 | 21 | val affectHandFov by register(default = true, Codec.BOOL) 22 | 23 | val retainZoomSteps by register(default = false, Codec.BOOL) 24 | val linearLikeSteps by register(default = true, Codec.BOOL) 25 | 26 | val scrollZoom by register(default = true, Codec.BOOL) 27 | val scrollZoomAmount by register(default = 3, Codec.INT) 28 | val scrollZoomSmoothness by register(default = 70, Codec.INT) 29 | 30 | val zoomKeyBehaviour by register(default = ZoomKeyBehaviour.HOLD, ZoomKeyBehaviour.CODEC) 31 | 32 | var keybindScrolling = false 33 | val _keybindScrolling by register(default = keybindScrolling, codec = Codec.BOOL) 34 | 35 | val relativeSensitivity by register(default = 100, Codec.INT) 36 | val relativeViewBobbing by register(default = true, Codec.BOOL) 37 | 38 | val cinematicCamera by register(default = 0, Codec.INT) 39 | 40 | val spyglassBehaviour by register(default = SpyglassBehaviour.COMBINE, SpyglassBehaviour.CODEC) 41 | val spyglassOverlayVisibility by register(default = OverlayVisibility.HOLDING, OverlayVisibility.CODEC) 42 | val spyglassSoundBehaviour by register(default = SoundBehaviour.WITH_OVERLAY, SoundBehaviour.CODEC) 43 | 44 | val secondaryZoomAmount by register(default = 4, Codec.INT) 45 | val secondaryZoomInTime by register(default = 10.0, Codec.DOUBLE) 46 | val secondaryZoomOutTime by register(default = 1.0, Codec.DOUBLE) 47 | val secondaryHideHUDOnZoom by register(default = true, Codec.BOOL) 48 | 49 | var firstLaunch = false 50 | val _firstLaunch by register(default = true, Codec.BOOL) 51 | 52 | final val allSettings = arrayOf( 53 | initialZoom, 54 | zoomInTime, 55 | zoomOutTime, 56 | zoomInTransition, 57 | zoomOutTransition, 58 | affectHandFov, 59 | retainZoomSteps, 60 | linearLikeSteps, 61 | scrollZoom, 62 | scrollZoomAmount, 63 | scrollZoomSmoothness, 64 | zoomKeyBehaviour, 65 | _keybindScrolling, 66 | relativeSensitivity, 67 | relativeViewBobbing, 68 | cinematicCamera, 69 | spyglassBehaviour, 70 | spyglassOverlayVisibility, 71 | spyglassSoundBehaviour, 72 | secondaryZoomAmount, 73 | secondaryZoomInTime, 74 | secondaryZoomOutTime, 75 | secondaryHideHUDOnZoom, 76 | _firstLaunch, 77 | ) 78 | 79 | constructor(settings: ZoomifySettings) : this() { 80 | this.initialZoom.value = settings.initialZoom.value 81 | this.zoomInTime.value = settings.zoomInTime.value 82 | this.zoomOutTime.value = settings.zoomOutTime.value 83 | this.zoomInTransition.value = settings.zoomInTransition.value 84 | this.zoomOutTransition.value = settings.zoomOutTransition.value 85 | this.affectHandFov.value = settings.affectHandFov.value 86 | this.retainZoomSteps.value = settings.retainZoomSteps.value 87 | this.linearLikeSteps.value = settings.linearLikeSteps.value 88 | this.scrollZoom.value = settings.scrollZoom.value 89 | this.scrollZoomAmount.value = settings.scrollZoomAmount.value 90 | this.scrollZoomSmoothness.value = settings.scrollZoomSmoothness.value 91 | this.zoomKeyBehaviour.value = settings.zoomKeyBehaviour.value 92 | this.keybindScrolling = settings.keybindScrolling 93 | this._keybindScrolling.value = settings._keybindScrolling.value 94 | this.relativeSensitivity.value = settings.relativeSensitivity.value 95 | this.relativeViewBobbing.value = settings.relativeViewBobbing.value 96 | this.cinematicCamera.value = settings.cinematicCamera.value 97 | this.spyglassBehaviour.value = settings.spyglassBehaviour.value 98 | this.spyglassOverlayVisibility.value = settings.spyglassOverlayVisibility.value 99 | this.spyglassSoundBehaviour.value = settings.spyglassSoundBehaviour.value 100 | this.secondaryZoomAmount.value = settings.secondaryZoomAmount.value 101 | this.secondaryZoomInTime.value = settings.secondaryZoomInTime.value 102 | this.secondaryZoomOutTime.value = settings.secondaryZoomOutTime.value 103 | this.secondaryHideHUDOnZoom.value = settings.secondaryHideHUDOnZoom.value 104 | this.firstLaunch = settings.firstLaunch 105 | this._firstLaunch.value = settings._firstLaunch.value 106 | } 107 | 108 | companion object : ZoomifySettings() { 109 | init { 110 | if (!loadFromFile()) { 111 | saveToFile() 112 | } 113 | 114 | if (_firstLaunch.value) { 115 | firstLaunch = true 116 | _firstLaunch.value = false 117 | saveToFile() 118 | } 119 | 120 | _keybindScrolling.value = keybindScrolling 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/demo/ControlEmulation.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.demo 2 | 3 | fun interface ControlEmulation { 4 | fun tick(imageRenderer: ZoomDemoImageRenderer) 5 | 6 | fun pause(imageRenderer: ZoomDemoImageRenderer) {} 7 | 8 | fun setup(imageRenderer: ZoomDemoImageRenderer) {} 9 | 10 | object InitialOnly : ControlEmulation { 11 | private var switchPauseTicks = 0 12 | 13 | override fun tick(imageRenderer: ZoomDemoImageRenderer) { 14 | // if the previous state is identical to the current state, pause for a bit, then inverse what we just did 15 | if (switchPauseTicks > 0) { 16 | switchPauseTicks-- 17 | if (switchPauseTicks == 0) { 18 | imageRenderer.keyDown = !imageRenderer.keyDown 19 | } 20 | } else if (imageRenderer.zoomHelper.getZoomDivisor(1f) == imageRenderer.zoomHelper.getZoomDivisor(0f)) { 21 | switchPauseTicks = 20 22 | } 23 | } 24 | 25 | override fun pause(imageRenderer: ZoomDemoImageRenderer) { 26 | imageRenderer.keyDown = false 27 | switchPauseTicks = 20 28 | } 29 | 30 | override fun setup(imageRenderer: ZoomDemoImageRenderer) { 31 | imageRenderer.keyDown = true 32 | } 33 | } 34 | 35 | object ScrollOnly : ControlEmulation { 36 | private var scrollPauseTicks = 0 37 | private var reverse = false 38 | 39 | override fun tick(imageRenderer: ZoomDemoImageRenderer) { 40 | if (imageRenderer.zoomHelper.maxScrollTiers() == 0) { 41 | imageRenderer.zoomHelper.setToZero(initial = false, scroll = true) 42 | imageRenderer.scrollTiers = 0 43 | return 44 | } 45 | 46 | if (scrollPauseTicks > 0) { 47 | scrollPauseTicks-- 48 | } else { 49 | scrollPauseTicks = 3 50 | 51 | if (!reverse) { 52 | imageRenderer.scrollTiers++ 53 | if (imageRenderer.scrollTiers >= imageRenderer.zoomHelper.maxScrollTiers()) { 54 | reverse = true 55 | scrollPauseTicks = 20 56 | } 57 | } else { 58 | imageRenderer.scrollTiers-- 59 | if (imageRenderer.scrollTiers <= 0) { 60 | reverse = false 61 | scrollPauseTicks = 20 62 | } 63 | } 64 | } 65 | } 66 | 67 | 68 | override fun setup(imageRenderer: ZoomDemoImageRenderer) { 69 | imageRenderer.keyDown = true 70 | imageRenderer.zoomHelper.skipInitial() 71 | } 72 | 73 | override fun pause(imageRenderer: ZoomDemoImageRenderer) { 74 | scrollPauseTicks = 20 75 | reverse = false 76 | imageRenderer.scrollTiers = 0 77 | imageRenderer.zoomHelper.skipInitial() 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/demo/ZoomDemoImageRenderer.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.demo 2 | 3 | import dev.isxander.yacl3.gui.image.ImageRenderer 4 | import dev.isxander.yacl3.gui.image.ImageRendererManager 5 | import dev.isxander.yacl3.gui.image.impl.AnimatedDynamicTextureImage 6 | import dev.isxander.zoomify.utils.* 7 | import dev.isxander.zoomify.zoom.ZoomHelper 8 | import net.minecraft.client.gui.GuiGraphics 9 | import net.minecraft.resources.ResourceLocation 10 | import java.util.Optional 11 | import java.util.concurrent.CompletableFuture 12 | 13 | abstract class ZoomDemoImageRenderer(val zoomHelper: ZoomHelper, private val zoomControl: ControlEmulation) : ImageRenderer { 14 | var keyDown = false 15 | var scrollTiers = 0 16 | 17 | init { 18 | zoomControl.setup(this) 19 | } 20 | 21 | abstract override fun render(graphics: GuiGraphics, x: Int, y: Int, renderWidth: Int, deltaTime: Float): Int 22 | 23 | override fun tick() { 24 | zoomHelper.tick(keyDown, scrollTiers) 25 | zoomControl.tick(this) 26 | } 27 | 28 | fun pause() { 29 | zoomHelper.setToZero() 30 | zoomControl.pause(this) 31 | } 32 | 33 | companion object { 34 | @JvmStatic 35 | protected fun makeWebp(id: ResourceLocation): CompletableFuture { 36 | return ImageRendererManager.registerOrGetImage(id) { AnimatedDynamicTextureImage.createWEBPFromTexture(id) } 37 | } 38 | } 39 | 40 | } 41 | 42 | class FirstPersonDemo(zoomHelper: ZoomHelper, zoomControl: ControlEmulation) : ZoomDemoImageRenderer(zoomHelper, zoomControl) { 43 | var keepHandFov = false 44 | 45 | private val handRenderer = makeWebp(HAND_TEXTURE) 46 | private val worldRenderer = makeWebp(WORLD_TEXTURE) 47 | 48 | override fun render(graphics: GuiGraphics, x: Int, y: Int, renderWidth: Int, deltaTime: Float): Int { 49 | val ratio = renderWidth / TEX_WIDTH.toDouble() 50 | val renderHeight = (TEX_HEIGHT * ratio).toInt() 51 | if (!handRenderer.isDone || !worldRenderer.isDone) { 52 | return renderHeight 53 | } 54 | 55 | graphics.enableScissor(x, y, x + renderWidth, y + renderHeight) 56 | 57 | graphics.pushPose() 58 | graphics.translate(x.toDouble(), y.toDouble(), 0.0) 59 | graphics.scale(ratio.toFloat(), ratio.toFloat(), 1f) 60 | 61 | val zoomScale = zoomHelper.getZoomDivisor(deltaTime).toFloat() 62 | graphics.pushPose() 63 | graphics.translate(TEX_WIDTH / 2f, TEX_HEIGHT / 2f, 0.0F) 64 | graphics.scale(zoomScale, zoomScale, 1.0F) 65 | graphics.translate(-TEX_WIDTH / 2f, -TEX_HEIGHT / 2f, 0.0F) 66 | 67 | worldRenderer.get().render(graphics, 0, 0, TEX_WIDTH, deltaTime) 68 | 69 | if (keepHandFov) graphics.popPose() 70 | handRenderer.get().render(graphics, 0, 0, TEX_WIDTH, deltaTime) 71 | if (!keepHandFov) graphics.popPose() 72 | 73 | graphics.popPose() 74 | 75 | graphics.disableScissor() 76 | 77 | return renderHeight 78 | } 79 | 80 | override fun close() { 81 | Optional.ofNullable(worldRenderer.getNow(null)).map { it.close() } 82 | Optional.ofNullable(handRenderer.getNow(null)).map { it.close() } 83 | } 84 | 85 | companion object { 86 | const val TEX_WIDTH = 1916 87 | const val TEX_HEIGHT = 910 88 | 89 | val WORLD_TEXTURE = zoomifyRl("textures/demo/zoom-world.webp") 90 | val HAND_TEXTURE = zoomifyRl("textures/demo/zoom-hand.webp") 91 | 92 | } 93 | } 94 | 95 | class ThirdPersonDemo(zoomHelper: ZoomHelper, zoomControl: ControlEmulation) : ZoomDemoImageRenderer(zoomHelper, zoomControl) { 96 | private val thirdPersonViewRenderer = makeWebp(PLAYER_VIEW) 97 | private val hudRenderer = makeWebp(HUD_TEXTURE) 98 | 99 | var renderHud = true 100 | 101 | override fun render(graphics: GuiGraphics, x: Int, y: Int, renderWidth: Int, deltaTime: Float): Int { 102 | val ratio = renderWidth / TEX_WIDTH.toDouble() 103 | val renderHeight = (TEX_HEIGHT * ratio).toInt() 104 | if (!thirdPersonViewRenderer.isDone || !hudRenderer.isDone) { 105 | return renderHeight 106 | } 107 | 108 | graphics.enableScissor(x, y, x + renderWidth, y + renderHeight) 109 | 110 | graphics.pushPose() 111 | graphics.translate(x.toDouble(), y.toDouble(), 0.0) 112 | graphics.scale(ratio.toFloat(), ratio.toFloat(), 1f) 113 | 114 | val zoomScale = zoomHelper.getZoomDivisor(deltaTime).toFloat() 115 | graphics.pushPose() 116 | graphics.translate(FirstPersonDemo.TEX_WIDTH / 2f, FirstPersonDemo.TEX_HEIGHT / 2f, 0.0F) 117 | graphics.scale(zoomScale, zoomScale, 1.0F) 118 | graphics.translate(-FirstPersonDemo.TEX_WIDTH / 2f, -FirstPersonDemo.TEX_HEIGHT / 2f, 0.0F) 119 | 120 | thirdPersonViewRenderer.get().render(graphics, 0, 0, TEX_WIDTH, deltaTime) 121 | 122 | graphics.popPose() 123 | 124 | if (renderHud) 125 | hudRenderer.get().render(graphics, 0, 0, TEX_WIDTH, deltaTime) 126 | 127 | graphics.popPose() 128 | graphics.disableScissor() 129 | 130 | return renderHeight 131 | } 132 | 133 | override fun close() { 134 | Optional.ofNullable(thirdPersonViewRenderer.getNow(null)).map { it.close() } 135 | Optional.ofNullable(hudRenderer.getNow(null)).map { it.close() } 136 | } 137 | 138 | companion object { 139 | const val TEX_WIDTH = 1915 140 | const val TEX_HEIGHT = 910 141 | 142 | val PLAYER_VIEW = zoomifyRl("textures/demo/third-person-view.webp") 143 | val HUD_TEXTURE = zoomifyRl("textures/demo/third-person-hud.webp") 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/migrator/Migration.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.migrator 2 | 3 | import net.minecraft.ChatFormatting 4 | import net.minecraft.network.chat.Component 5 | 6 | class Migration { 7 | private val warnings = mutableListOf() 8 | private val errors = mutableListOf() 9 | var requireRestart = false 10 | private set 11 | 12 | fun error(text: Component) = errors.add(text) 13 | fun warn(text: Component) = warnings.add(text) 14 | 15 | fun requireRestart() { 16 | requireRestart = true 17 | } 18 | 19 | fun generateReport(): Component { 20 | val text = Component.empty() 21 | 22 | val errors = this.errors 23 | if (requireRestart) { 24 | errors.add(0, Component.translatable("zoomify.migrate.restart")) 25 | } 26 | 27 | for (error in errors) { 28 | val line = Component.empty() 29 | .append("\u25C6 ") 30 | .append(error) 31 | .append("\n") 32 | .withStyle(ChatFormatting.RED) 33 | text.append(line) 34 | } 35 | 36 | for (warning in warnings) { 37 | val line = Component.empty() 38 | .append("\u25C6 ") 39 | .append(warning) 40 | .append("\n") 41 | .withStyle(ChatFormatting.YELLOW) 42 | text.append(line) 43 | } 44 | 45 | return text 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/migrator/MigrationAvailableScreen.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.migrator 2 | 3 | import dev.isxander.zoomify.config.ZoomifySettings 4 | import net.minecraft.ChatFormatting 5 | import net.minecraft.client.Minecraft 6 | import net.minecraft.client.gui.screens.ConfirmScreen 7 | import net.minecraft.client.gui.screens.Screen 8 | import net.minecraft.network.chat.Component 9 | 10 | class MigrationAvailableScreen(migrator: Migrator, parent: Screen?) : ConfirmScreen( 11 | { yes -> 12 | if (yes) { 13 | val migration = Migration() 14 | migrator.migrate(migration) 15 | ZoomifySettings.saveToFile() 16 | Minecraft.getInstance().setScreen(MigrationResultScreen(migration, parent)) 17 | } else { 18 | Minecraft.getInstance().setScreen(parent) 19 | } 20 | }, 21 | Component.translatable("zoomify.migrate.available.title", migrator.name.copy().withStyle(ChatFormatting.BOLD)), 22 | Component.translatable("zoomify.migrate.available.message", migrator.name) 23 | ) 24 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/migrator/MigrationResultScreen.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.migrator 2 | 3 | import net.minecraft.client.Minecraft 4 | import net.minecraft.client.gui.screens.AlertScreen 5 | import net.minecraft.client.gui.screens.Screen 6 | import net.minecraft.network.chat.CommonComponents 7 | import net.minecraft.network.chat.Component 8 | 9 | 10 | class MigrationResultScreen( 11 | migration: Migration, 12 | parent: Screen?, 13 | ) : AlertScreen( 14 | { 15 | if (migration.requireRestart) 16 | Minecraft.getInstance().stop() 17 | else 18 | Minecraft.getInstance().setScreen(parent) 19 | }, 20 | Component.translatable("zoomify.migrate.result.title"), 21 | migration.generateReport(), 22 | if (migration.requireRestart) 23 | Component.translatable("zoomify.migrate.result.restart_game") 24 | else 25 | CommonComponents.GUI_DONE, 26 | true 27 | ) 28 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/migrator/Migrator.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.migrator 2 | 3 | import dev.isxander.zoomify.config.migrator.impl.OkZoomerMigrator 4 | import net.minecraft.client.Minecraft 5 | import net.minecraft.network.chat.Component 6 | 7 | interface Migrator { 8 | val name: Component 9 | 10 | fun isMigrationAvailable(): Boolean 11 | 12 | fun migrate(migration: Migration) 13 | 14 | companion object { 15 | val MIGRATORS = listOf(OkZoomerMigrator) 16 | 17 | fun checkMigrations(): Boolean { 18 | val available = MIGRATORS.filter { it.isMigrationAvailable() } 19 | 20 | if (available.isEmpty()) 21 | return false 22 | 23 | var lastScreen = Minecraft.getInstance().screen 24 | 25 | for (migrator in available) { 26 | lastScreen = MigrationAvailableScreen(migrator, lastScreen) 27 | } 28 | 29 | Minecraft.getInstance().setScreen(lastScreen) 30 | return true 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/config/migrator/impl/OkZoomerMigrator.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.config.migrator.impl 2 | 3 | import com.akuleshov7.ktoml.Toml 4 | import com.akuleshov7.ktoml.TomlInputConfig 5 | import dev.isxander.yacl3.config.v3.value 6 | import dev.isxander.zoomify.Zoomify 7 | import dev.isxander.zoomify.config.* 8 | import dev.isxander.zoomify.config.migrator.Migration 9 | import dev.isxander.zoomify.config.migrator.Migrator 10 | import kotlinx.serialization.SerialName 11 | import kotlinx.serialization.Serializable 12 | import kotlinx.serialization.decodeFromString 13 | import net.fabricmc.loader.api.FabricLoader 14 | import java.nio.file.Files 15 | import dev.isxander.zoomify.config.migrator.impl.OkZoomerMigrator.OkZoomerConfig.Features.CinematicCameraState 16 | import dev.isxander.zoomify.config.migrator.impl.OkZoomerMigrator.OkZoomerConfig.Features.ZoomMode 17 | import dev.isxander.zoomify.config.migrator.impl.OkZoomerMigrator.OkZoomerConfig.Features.Overlay 18 | import dev.isxander.zoomify.config.migrator.impl.OkZoomerMigrator.OkZoomerConfig.Features.SpyglassDependency 19 | import dev.isxander.zoomify.utils.TransitionType 20 | import net.minecraft.network.chat.Component 21 | import kotlin.math.roundToInt 22 | 23 | object OkZoomerMigrator : Migrator { 24 | private val file = FabricLoader.getInstance().configDir.resolve("ok_zoomer/config.toml") 25 | private val toml = Toml( 26 | inputConfig = TomlInputConfig( 27 | ignoreUnknownNames = true 28 | ) 29 | ) 30 | 31 | override val name: Component = Component.translatable("zoomify.migrate.okz") 32 | 33 | override fun isMigrationAvailable(): Boolean = 34 | Files.exists(file) 35 | 36 | override fun migrate(migration: Migration) { 37 | val okz = toml.decodeFromString(Files.readString(file)) 38 | 39 | when (okz.features.cinematicCamera) { 40 | CinematicCameraState.VANILLA -> 41 | ZoomifySettings.cinematicCamera.value = 100 42 | CinematicCameraState.MULTIPLIED -> 43 | ZoomifySettings.cinematicCamera.value = (1 / okz.values.cinematicMultiplier * 100).toInt() 44 | CinematicCameraState.OFF -> 45 | ZoomifySettings.cinematicCamera.value = 0 46 | } 47 | 48 | if (okz.features.reduceSensitivity) { 49 | ZoomifySettings.relativeSensitivity.value = 100 50 | } else { 51 | ZoomifySettings.relativeSensitivity.value = 0 52 | } 53 | 54 | when (okz.features.zoomMode) { 55 | ZoomMode.HOLD -> 56 | ZoomifySettings.zoomKeyBehaviour.value = ZoomKeyBehaviour.HOLD 57 | ZoomMode.TOGGLE -> 58 | ZoomifySettings.zoomKeyBehaviour.value = ZoomKeyBehaviour.TOGGLE 59 | ZoomMode.PERSISTENT -> { 60 | migration.error(Component.translatable("zoomify.migrate.okz.persistent")) 61 | } 62 | } 63 | 64 | ZoomifySettings._keybindScrolling.value = okz.features.extraKeyBinds 65 | migration.requireRestart() 66 | 67 | when (okz.features.zoomOverlay) { 68 | Overlay.OFF -> 69 | ZoomifySettings.spyglassOverlayVisibility.value = OverlayVisibility.NEVER 70 | Overlay.SPYGLASS -> 71 | ZoomifySettings.spyglassOverlayVisibility.value = when (okz.features.spyglassDependency) { 72 | SpyglassDependency.REPLACE_ZOOM -> OverlayVisibility.ALWAYS 73 | SpyglassDependency.REQUIRE_ITEM, SpyglassDependency.BOTH -> OverlayVisibility.HOLDING 74 | SpyglassDependency.OFF -> OverlayVisibility.ALWAYS 75 | } 76 | Overlay.VIGNETTE -> { 77 | migration.error(Component.translatable("zoomify.migrate.okz.vignette")) 78 | } 79 | } 80 | 81 | when (okz.features.spyglassDependency) { 82 | SpyglassDependency.OFF -> 83 | ZoomifySettings.spyglassBehaviour.value = SpyglassBehaviour.COMBINE 84 | SpyglassDependency.REPLACE_ZOOM -> 85 | ZoomifySettings.spyglassBehaviour.value = SpyglassBehaviour.OVERRIDE 86 | SpyglassDependency.REQUIRE_ITEM -> 87 | ZoomifySettings.spyglassBehaviour.value = SpyglassBehaviour.ONLY_ZOOM_WHILE_HOLDING 88 | SpyglassDependency.BOTH -> 89 | ZoomifySettings.spyglassBehaviour.value = SpyglassBehaviour.ONLY_ZOOM_WHILE_CARRYING // FIXME: idk what this does 90 | } 91 | 92 | ZoomifySettings.spyglassSoundBehaviour.value = 93 | if (okz.tweaks.useSpyglassSounds) 94 | SoundBehaviour.ALWAYS 95 | else 96 | SoundBehaviour.NEVER 97 | 98 | ZoomifySettings.initialZoom.value = okz.values.zoomDivisor.roundToInt() 99 | migration.warn(Component.translatable("zoomify.migrate.okz.minZoomDiv")) 100 | ZoomifySettings.scrollZoomAmount.value = ((okz.values.maxZoomDivisor - ZoomifySettings.initialZoom.value) / Zoomify.maxScrollTiers).roundToInt() 101 | 102 | migration.warn(Component.translatable("zoomify.migrate.okz.stepAmt")) 103 | 104 | when (okz.features.zoomTransition) { 105 | OkZoomerConfig.Features.TransitionMode.LINEAR -> { 106 | migration.error(Component.translatable("zoomify.migrate.okz.linearNotSupported")) 107 | ZoomifySettings.zoomInTransition.value = TransitionType.LINEAR 108 | ZoomifySettings.zoomOutTransition.value = TransitionType.LINEAR 109 | } 110 | OkZoomerConfig.Features.TransitionMode.SMOOTH -> { 111 | val targetMultiplier = 1f / ZoomifySettings.initialZoom.value 112 | var multiplier = 1f 113 | var ticks = 0 114 | while (multiplier != targetMultiplier) { 115 | multiplier += (targetMultiplier - multiplier) * okz.values.smoothMultiplier.toFloat() 116 | ticks++ 117 | } 118 | val zoomTime = (ticks * 0.05 / 0.1).roundToInt() * 0.1 119 | ZoomifySettings.zoomInTime.value = zoomTime 120 | ZoomifySettings.zoomOutTime.value = zoomTime 121 | ZoomifySettings.zoomInTransition.value = TransitionType.EASE_IN_EXP 122 | ZoomifySettings.zoomOutTransition.value = TransitionType.EASE_IN_EXP 123 | } 124 | OkZoomerConfig.Features.TransitionMode.OFF -> { 125 | ZoomifySettings.zoomInTime.value = 0.0 126 | ZoomifySettings.zoomOutTime.value = 0.0 127 | ZoomifySettings.zoomInTransition.value = TransitionType.INSTANT 128 | ZoomifySettings.zoomOutTransition.value = TransitionType.INSTANT 129 | } 130 | } 131 | 132 | 133 | ZoomifySettings.retainZoomSteps.value = !okz.tweaks.forgetZoomDivisor 134 | 135 | if (okz.tweaks.unbindConflictingKey) { 136 | Zoomify.unbindConflicting() 137 | } 138 | } 139 | 140 | @Serializable 141 | data class OkZoomerConfig( 142 | val features: Features, 143 | val values: Values, 144 | val tweaks: Tweaks, 145 | ) { 146 | @Serializable 147 | data class Features( 148 | @SerialName("cinematic_camera") val cinematicCamera: CinematicCameraState, 149 | @SerialName("reduce_sensitivity") val reduceSensitivity: Boolean, 150 | @SerialName("zoom_transition") val zoomTransition: TransitionMode, 151 | @SerialName("zoom_mode") val zoomMode: ZoomMode, 152 | @SerialName("zoom_scrolling") val zoomScrolling: Boolean, 153 | @SerialName("extra_key_binds") val extraKeyBinds: Boolean, 154 | @SerialName("zoom_overlay") val zoomOverlay: Overlay, 155 | @SerialName("spyglass_dependency") val spyglassDependency: SpyglassDependency 156 | ) { 157 | @Serializable 158 | enum class CinematicCameraState { 159 | OFF, VANILLA, MULTIPLIED 160 | } 161 | 162 | @Serializable 163 | enum class TransitionMode { 164 | OFF, SMOOTH, LINEAR 165 | } 166 | 167 | @Serializable 168 | enum class ZoomMode { 169 | HOLD, TOGGLE, PERSISTENT 170 | } 171 | 172 | @Serializable 173 | enum class Overlay { 174 | OFF, VIGNETTE, SPYGLASS 175 | } 176 | 177 | @Serializable 178 | enum class SpyglassDependency { 179 | OFF, REQUIRE_ITEM, REPLACE_ZOOM, BOTH 180 | } 181 | } 182 | 183 | @Serializable 184 | data class Values( 185 | @SerialName("zoom_divisor") val zoomDivisor: Double, 186 | @SerialName("minimum_zoom_divisor") val minZoomDivisor: Double, 187 | @SerialName("maximum_zoom_divisor") val maxZoomDivisor: Double, 188 | @SerialName("upper_scroll_steps") val upperScrollSteps: Long, 189 | @SerialName("lower_scroll_steps") val lowerScrollSteps: Long, 190 | @SerialName("smooth_multiplier") val smoothMultiplier: Double, 191 | @SerialName("cinematic_multiplier") val cinematicMultiplier: Double, 192 | @SerialName("minimum_linear_step") val minLinearStep: Double, 193 | @SerialName("maximum_linear_step") val maxLinearSteps: Double, 194 | ) 195 | 196 | @Serializable 197 | data class Tweaks( 198 | @SerialName("reset_zoom_with_mouse") val resetZoomWithMouse: Boolean, 199 | @SerialName("forget_zoom_divisor") val forgetZoomDivisor: Boolean, 200 | @SerialName("unbind_conflicting_key") val unbindConflictingKey: Boolean, 201 | @SerialName("use_spyglass_texture") val useSpyglassTexture: Boolean, 202 | @SerialName("use_spyglass_sounds") val useSpyglassSounds: Boolean, 203 | ) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/integrations/ControlifyIntegration.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.integrations 2 | 3 | import dev.isxander.controlify.api.ControlifyApi 4 | import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint 5 | import dev.isxander.controlify.api.event.ControlifyEvents 6 | import dev.isxander.yacl3.config.v3.value 7 | import dev.isxander.zoomify.Zoomify 8 | import dev.isxander.zoomify.config.ZoomifySettings 9 | import net.minecraft.util.Mth 10 | 11 | object ControlifyIntegration : ControlifyEntrypoint { 12 | override fun onControlifyInit(controlify: ControlifyApi) { 13 | ControlifyEvents.LOOK_INPUT_MODIFIER.register { 14 | it.lookInput.x /= Mth.lerp(ZoomifySettings.relativeSensitivity.value / 100.0, 1.0, Zoomify.previousZoomDivisor).toFloat() 15 | it.lookInput.y /= Mth.lerp(ZoomifySettings.relativeSensitivity.value / 100.0, 1.0, Zoomify.previousZoomDivisor).toFloat() 16 | } 17 | } 18 | 19 | override fun onControllersDiscovered(controlify: ControlifyApi) { 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/integrations/IntegrationHelper.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.integrations 2 | 3 | import net.fabricmc.loader.api.FabricLoader 4 | import net.fabricmc.loader.api.metadata.version.VersionPredicate 5 | 6 | fun constrainModVersionIfLoaded(modId: String, constraint: String) { 7 | val predicate = VersionPredicate.parse(constraint) 8 | 9 | val modContainer = FabricLoader.getInstance().getModContainer(modId) 10 | if (modContainer.isPresent) { 11 | val version = modContainer.get().metadata.version 12 | if (!predicate.test(version)) { 13 | throw IllegalStateException("Mod $modId is not constrained to version $constraint") 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/integrations/ModMenuIntegration.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.integrations 2 | 3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory 4 | import com.terraformersmc.modmenu.api.ModMenuApi 5 | import dev.isxander.zoomify.config.createSettingsGui 6 | 7 | object ModMenuIntegration : ModMenuApi { 8 | override fun getModConfigScreenFactory(): ConfigScreenFactory<*> = ConfigScreenFactory { parent -> 9 | createSettingsGui(parent) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/utils/KeySource.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.utils 2 | 3 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper 4 | import net.minecraft.client.KeyMapping 5 | 6 | interface KeySource { 7 | val justPressed: Boolean 8 | 9 | val isDown: Boolean 10 | } 11 | 12 | class MultiKeySource(sources: List) : KeySource { 13 | private val sources = sources.toMutableList() 14 | 15 | constructor(vararg sources: KeySource) : this(sources.toList()) 16 | constructor() : this(emptyList()) 17 | 18 | override val justPressed: Boolean 19 | get() = sources.any { it.justPressed } 20 | 21 | override val isDown: Boolean 22 | get() = sources.any { it.isDown } 23 | 24 | fun addSource(source: KeySource) { 25 | sources.add(source) 26 | } 27 | } 28 | 29 | fun KeyMapping.toKeySource(register: Boolean = false) = object : KeySource { 30 | override val justPressed: Boolean 31 | get() = this@toKeySource.consumeClick() 32 | 33 | override val isDown: Boolean 34 | get() = this@toKeySource.isDown 35 | }.also { if (register) KeyBindingHelper.registerKeyBinding(this) } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/utils/MinecraftExt.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.utils 2 | 3 | import net.minecraft.client.Minecraft 4 | import net.minecraft.client.gui.GuiGraphics 5 | import net.minecraft.client.gui.components.toasts.SystemToast 6 | import net.minecraft.network.chat.Component 7 | import net.minecraft.resources.ResourceLocation 8 | 9 | val minecraft: Minecraft = Minecraft.getInstance() 10 | 11 | fun GuiGraphics.pushPose() = pose().pushPose() 12 | fun GuiGraphics.popPose() = pose().popPose() 13 | fun GuiGraphics.translate(x: Double, y: Double, z: Double) = pose().translate(x, y, z) 14 | fun GuiGraphics.translate(x: Float, y: Float, z: Float) = pose().translate(x, y, z) 15 | fun GuiGraphics.scale(x: Float, y: Float, z: Float) = pose().scale(x, y, z) 16 | 17 | // i love kotlin 18 | typealias ToastTypes = 19 | //? if >1.20.1 { 20 | SystemToast.SystemToastId 21 | //?} else { 22 | /*SystemToast.SystemToastIds 23 | *///?} 24 | 25 | fun toast( 26 | title: Component, 27 | description: Component, 28 | longer: Boolean = false 29 | ): SystemToast = SystemToast.multiline( 30 | minecraft, 31 | if (longer) ToastTypes.UNSECURE_SERVER_WARNING else ToastTypes.PERIODIC_NOTIFICATION, 32 | title, 33 | description 34 | ).also { 35 | val toastManager = /*? if >=1.21.2 {*/ minecraft.toastManager /*?} else {*/ /*minecraft.toasts *//*?}*/ 36 | toastManager.addToast(it) 37 | } 38 | 39 | fun zoomifyRl(path: String) = 40 | //? if >=1.21 { 41 | ResourceLocation.fromNamespaceAndPath("zoomify", path) 42 | //?} else { 43 | /*ResourceLocation("zoomify", path) 44 | *///?} 45 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/utils/NameableEnumKt.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.utils 2 | 3 | import dev.isxander.yacl3.api.NameableEnum 4 | import net.minecraft.network.chat.Component 5 | 6 | interface NameableEnumKt : NameableEnum { 7 | val localisedName: Component 8 | 9 | override fun getDisplayName(): Component = localisedName 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/utils/TransitionType.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.utils 2 | 3 | import dev.isxander.yacl3.api.NameableEnum 4 | import net.minecraft.network.chat.Component 5 | import net.minecraft.util.StringRepresentable 6 | import kotlin.math.* 7 | 8 | enum class TransitionType(val localisedName: Component) : Transition, NameableEnum, StringRepresentable { 9 | INSTANT("zoomify.transition.instant") { 10 | override fun apply(t: Double) = 11 | t 12 | }, 13 | LINEAR("zoomify.transition.linear") { 14 | override fun apply(t: Double) = 15 | t 16 | }, 17 | EASE_IN_SINE("zoomify.transition.ease_in_sine") { 18 | override fun apply(t: Double): Double = 19 | 1 - cos((t * PI) / 2) 20 | 21 | override fun inverse(x: Double) = 22 | acos(-(x - 1)) * 2 / PI 23 | }, 24 | EASE_OUT_SINE("zoomify.transition.ease_out_sine") { 25 | override fun apply(t: Double): Double = 26 | sin((t * PI) / 2) 27 | 28 | override fun inverse(x: Double) = 29 | asin(x) * 2 / PI 30 | }, 31 | EASE_IN_OUT_SINE("zoomify.transition.ease_in_out_sine") { 32 | override fun apply(t: Double): Double = 33 | -(cos(PI * t) - 1) / 2 34 | }, 35 | EASE_IN_QUAD("zoomify.transition.ease_in_quad") { 36 | override fun apply(t: Double): Double = 37 | t * t 38 | 39 | override fun inverse(x: Double) = 40 | sqrt(x) 41 | }, 42 | EASE_OUT_QUAD("zoomify.transition.ease_out_quad") { 43 | override fun apply(t: Double): Double = 44 | 1 - (1 - t) * (1 - t) 45 | 46 | override fun inverse(x: Double) = 47 | -(sqrt(-(x - 1)) - 1) 48 | 49 | }, 50 | EASE_IN_OUT_QUAD("zoomify.transition.ease_in_out_quad") { 51 | override fun apply(t: Double): Double = 52 | if (t < 0.5) 53 | 2 * t * t 54 | else 55 | 1 - (-2 * t + 2).pow(2) / 2 56 | }, 57 | EASE_IN_CUBIC("zoomify.transition.ease_in_cubic") { 58 | override fun apply(t: Double): Double = 59 | t.pow(3) 60 | 61 | override fun inverse(x: Double) = 62 | x.pow(1 / 3.0) 63 | }, 64 | EASE_OUT_CUBIC("zoomify.transition.ease_out_cubic") { 65 | override fun apply(t: Double): Double = 66 | 1 - (1 - t).pow(3) 67 | 68 | override fun inverse(x: Double) = 69 | -((-x + 1).pow(1.0 / 3.0)) + 1 70 | }, 71 | EASE_IN_OUT_CUBIC("zoomify.transition.ease_in_out_cubic") { 72 | override fun apply(t: Double): Double = 73 | if (t < 0.5) 74 | 4 * t * t * t 75 | else 76 | 1 - (-2 * t + 2).pow(3) / 2 77 | }, 78 | EASE_IN_EXP("zoomify.transition.ease_in_exp") { 79 | private val c_log2_1023 = log(1023.0, base=2.0) 80 | 81 | override fun apply(t: Double): Double = 82 | when (t) { 83 | 0.0 -> 0.0 84 | 1.0 -> 1.0 85 | else -> 2.0.pow(10.0 * t - c_log2_1023) - 1/1023 86 | } 87 | 88 | override fun inverse(x: Double) = 89 | when (x) { 90 | 0.0 -> 0.0 91 | 1.0 -> 1.0 92 | else -> ln(1023 * x + 1) / (10 * ln(2.0)) 93 | } 94 | }, 95 | EASE_OUT_EXP("zoomify.transition.ease_out_exp") { 96 | private val c_log2_1023 = log(1023.0, base=2.0) 97 | private val c_10_ln2 = 10.0 * ln(2.0) 98 | private val c_ln_1203 = ln(1023.0) 99 | 100 | override fun apply(t: Double): Double = 101 | when (t) { 102 | 0.0 -> 0.0 103 | 1.0 -> 1.0 104 | else -> 1.0 - 2.0.pow(10.0 - c_log2_1023 - 10.0 * t) + 1/1023 105 | } 106 | 107 | override fun inverse(x: Double) = 108 | when (x) { 109 | 0.0 -> 0.0 110 | 1.0 -> 1.0 111 | else -> -((ln(-((1023 * x - 1024) / 1023)) - c_10_ln2 + c_ln_1203) / c_10_ln2) 112 | } 113 | }, 114 | EASE_IN_OUT_EXP("zoomify.transition.ease_in_out_exp") { 115 | private val c_log2_1023 = log(1023.0, base=2.0) 116 | 117 | override fun apply(t: Double): Double = 118 | when { 119 | t == 0.0 -> 0.0 120 | t == 1.0 -> 1.0 121 | t < 0.5 -> 2.0.pow(20.0 * t - c_log2_1023) - 1/1023 122 | else -> 1.0 - 2.0.pow(10.0 - c_log2_1023 - 10.0 * t) + 1/1023 123 | } 124 | }; 125 | 126 | constructor(name: String) : this(Component.translatable(name)) 127 | 128 | override fun getSerializedName(): String = name.lowercase() 129 | override fun getDisplayName(): Component = localisedName 130 | 131 | fun opposite(): TransitionType = when (this) { 132 | INSTANT -> INSTANT 133 | LINEAR -> LINEAR 134 | EASE_IN_SINE -> EASE_OUT_SINE 135 | EASE_OUT_SINE -> EASE_IN_SINE 136 | EASE_IN_OUT_SINE -> EASE_IN_OUT_SINE 137 | EASE_IN_QUAD -> EASE_OUT_QUAD 138 | EASE_OUT_QUAD -> EASE_IN_QUAD 139 | EASE_IN_OUT_QUAD -> EASE_IN_OUT_QUAD 140 | EASE_IN_CUBIC -> EASE_OUT_CUBIC 141 | EASE_OUT_CUBIC -> EASE_IN_CUBIC 142 | EASE_IN_OUT_CUBIC -> EASE_IN_OUT_CUBIC 143 | EASE_IN_EXP -> EASE_OUT_EXP 144 | EASE_OUT_EXP -> EASE_IN_EXP 145 | EASE_IN_OUT_EXP -> EASE_IN_OUT_EXP 146 | } 147 | 148 | companion object { 149 | val CODEC = StringRepresentable.fromEnum(::values) 150 | } 151 | } 152 | 153 | fun interface Transition { 154 | fun apply(t: Double): Double 155 | 156 | fun inverse(x: Double): Double { 157 | throw UnsupportedOperationException() 158 | } 159 | 160 | fun hasInverse() = try { 161 | inverse(0.0) 162 | true 163 | } catch (_: UnsupportedOperationException) { 164 | false 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/zoom/DefaultZoomHelpers.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.zoom 2 | 3 | import dev.isxander.yacl3.config.v3.value 4 | import dev.isxander.zoomify.Zoomify 5 | import dev.isxander.zoomify.config.ZoomifySettings 6 | import net.minecraft.util.Mth 7 | 8 | fun RegularZoomHelper(settings: ZoomifySettings) = ZoomHelper( 9 | TransitionInterpolator( 10 | settings.zoomInTransition::value, 11 | settings.zoomOutTransition::value, 12 | settings.zoomInTime::value, 13 | settings.zoomOutTime::value 14 | ), 15 | SmoothInterpolator { 16 | Mth.lerp( 17 | settings.scrollZoomSmoothness.value / 100.0, 18 | 1.0, 19 | 0.1 20 | ) 21 | }, 22 | initialZoom = settings.initialZoom::value, 23 | scrollZoomAmount = settings.scrollZoomAmount::value, 24 | maxScrollTiers = Zoomify::maxScrollTiers, 25 | linearLikeSteps = settings.linearLikeSteps::value, 26 | ) 27 | 28 | fun SecondaryZoomHelper(settings: ZoomifySettings) = ZoomHelper( 29 | TimedInterpolator(settings.secondaryZoomInTime::value, settings.secondaryZoomOutTime::value), 30 | InstantInterpolator, 31 | initialZoom = settings.secondaryZoomAmount::value, 32 | scrollZoomAmount = { 0 }, 33 | maxScrollTiers = { 0 }, 34 | linearLikeSteps = { false }, 35 | ) 36 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/zoom/Interpolator.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.zoom 2 | 3 | import dev.isxander.zoomify.utils.TransitionType 4 | import kotlin.math.max 5 | import kotlin.math.min 6 | 7 | interface Interpolator { 8 | fun tickInterpolation(targetInterpolation: Double, currentInterpolation: Double, tickDelta: Double): Double 9 | 10 | fun modifyInterpolation(interpolation: Double): Double = interpolation 11 | 12 | fun modifyPrevInterpolation(interpolation: Double): Double = interpolation 13 | val isSmooth: Boolean 14 | } 15 | 16 | object InstantInterpolator : Interpolator { 17 | override fun tickInterpolation(targetInterpolation: Double, currentInterpolation: Double, tickDelta: Double): Double { 18 | return targetInterpolation 19 | } 20 | 21 | override val isSmooth: Boolean = false 22 | } 23 | 24 | sealed class LinearInterpolator : Interpolator { 25 | protected var goingIn = true 26 | private set 27 | 28 | override fun tickInterpolation(targetInterpolation: Double, currentInterpolation: Double, tickDelta: Double): Double { 29 | if (targetInterpolation > currentInterpolation) { 30 | goingIn = true 31 | return min(currentInterpolation + getTimeIncrement(false, tickDelta, targetInterpolation, currentInterpolation), targetInterpolation) 32 | } else if (targetInterpolation < currentInterpolation) { 33 | goingIn = false 34 | return max(currentInterpolation - getTimeIncrement(true, tickDelta, targetInterpolation, currentInterpolation), targetInterpolation) 35 | } 36 | goingIn = true 37 | return targetInterpolation 38 | } 39 | 40 | override val isSmooth: Boolean = true 41 | 42 | abstract fun getTimeIncrement( 43 | zoomingOut: Boolean, 44 | tickDelta: Double, 45 | targetInterpolation: Double, 46 | currentInterpolation: Double 47 | ): Double 48 | } 49 | 50 | open class TimedInterpolator(val timeIn: () -> Double, val timeOut: () -> Double) : LinearInterpolator() { 51 | override fun getTimeIncrement( 52 | zoomingOut: Boolean, 53 | tickDelta: Double, 54 | targetInterpolation: Double, 55 | currentInterpolation: Double 56 | ): Double { 57 | return tickDelta / if (zoomingOut) timeOut() else timeIn() 58 | } 59 | 60 | override val isSmooth: Boolean 61 | get() = (goingIn && timeIn() > 0.0) || (!goingIn && timeOut() > 0.0) 62 | } 63 | 64 | class TransitionInterpolator(val transitionIn: () -> TransitionType, transitionOut: () -> TransitionType, timeIn: () -> Double, timeOut: () -> Double) : TimedInterpolator(timeIn, timeOut) { 65 | val transitionOut = { transitionOut().opposite() } 66 | private var activeTransition: TransitionType = transitionIn() 67 | private var inactiveTransition: TransitionType = transitionOut() 68 | private var prevTargetInterpolation: Double = 0.0 69 | private var justSwappedTransition = false 70 | 71 | override fun tickInterpolation( 72 | targetInterpolation: Double, 73 | currentInterpolation: Double, 74 | tickDelta: Double 75 | ): Double { 76 | var currentInterpolationMod = currentInterpolation 77 | 78 | if (targetInterpolation > currentInterpolation) { 79 | activeTransition = transitionIn() 80 | inactiveTransition = transitionOut() 81 | 82 | if (prevTargetInterpolation < targetInterpolation && activeTransition.hasInverse()) { 83 | justSwappedTransition = true 84 | currentInterpolationMod = activeTransition.inverse(inactiveTransition.apply(currentInterpolationMod)) 85 | } 86 | } else if (targetInterpolation < currentInterpolation) { 87 | activeTransition = transitionOut() 88 | inactiveTransition = transitionIn() 89 | 90 | if (prevTargetInterpolation > targetInterpolation && activeTransition.hasInverse()) { 91 | justSwappedTransition = true 92 | currentInterpolationMod = activeTransition.inverse(inactiveTransition.apply(currentInterpolationMod)) 93 | } 94 | } 95 | 96 | prevTargetInterpolation = targetInterpolation 97 | 98 | if (activeTransition == TransitionType.INSTANT) 99 | return targetInterpolation 100 | 101 | return super.tickInterpolation(targetInterpolation, currentInterpolationMod, tickDelta) 102 | } 103 | 104 | override fun modifyInterpolation(interpolation: Double): Double { 105 | return activeTransition.apply(interpolation) 106 | } 107 | 108 | override fun modifyPrevInterpolation(interpolation: Double): Double { 109 | if (justSwappedTransition) { 110 | justSwappedTransition = false 111 | return activeTransition.inverse(inactiveTransition.apply(interpolation)) 112 | } 113 | return interpolation 114 | } 115 | 116 | override val isSmooth: Boolean 117 | get() = !justSwappedTransition && super.isSmooth && activeTransition != TransitionType.INSTANT 118 | } 119 | 120 | class SmoothInterpolator(val smoothness: () -> Double) : LinearInterpolator() { 121 | override fun getTimeIncrement( 122 | zoomingOut: Boolean, 123 | tickDelta: Double, 124 | targetInterpolation: Double, 125 | currentInterpolation: Double 126 | ): Double { 127 | val diff = if (!zoomingOut) targetInterpolation - currentInterpolation else currentInterpolation - targetInterpolation 128 | return diff * smoothness() / 0.05 * tickDelta 129 | } 130 | 131 | override fun tickInterpolation( 132 | targetInterpolation: Double, 133 | currentInterpolation: Double, 134 | tickDelta: Double 135 | ): Double { 136 | if (!isSmooth) 137 | return targetInterpolation 138 | 139 | return super.tickInterpolation(targetInterpolation, currentInterpolation, tickDelta) 140 | } 141 | 142 | override val isSmooth: Boolean 143 | get() = smoothness() != 1.0 144 | } 145 | -------------------------------------------------------------------------------- /src/main/kotlin/dev/isxander/zoomify/zoom/ZoomHelper.kt: -------------------------------------------------------------------------------- 1 | package dev.isxander.zoomify.zoom 2 | 3 | import dev.isxander.zoomify.Zoomify 4 | import net.minecraft.util.Mth 5 | import kotlin.math.pow 6 | 7 | class ZoomHelper( 8 | private val initialInterpolator: Interpolator, 9 | private val scrollInterpolator: Interpolator, 10 | 11 | private val initialZoom: () -> Int, 12 | private val scrollZoomAmount: () -> Int, 13 | val maxScrollTiers: () -> Int, 14 | private val linearLikeSteps: () -> Boolean, 15 | ) { 16 | private var prevInitialInterpolation = 0.0 17 | private var initialInterpolation = 0.0 18 | 19 | private var zoomingLastTick = false 20 | 21 | private var prevScrollInterpolation = 0.0 22 | private var scrollInterpolation = 0.0 23 | private var lastScrollTier = 0 24 | 25 | private var resetting = false 26 | private var resetMultiplier = 0.0 27 | 28 | fun tick(zooming: Boolean, scrollTiers: Int, lastFrameDuration: Double = 0.05) { 29 | tickInitial(zooming, lastFrameDuration) 30 | tickScroll(scrollTiers, lastFrameDuration) 31 | } 32 | 33 | private fun tickInitial(zooming: Boolean, lastFrameDuration: Double) { 34 | if (zooming && !zoomingLastTick) 35 | resetting = false 36 | 37 | val targetZoom = if (zooming) 1.0 else 0.0 38 | prevInitialInterpolation = initialInterpolation 39 | initialInterpolation = 40 | initialInterpolator.tickInterpolation(targetZoom, initialInterpolation, lastFrameDuration) 41 | prevInitialInterpolation = initialInterpolator.modifyPrevInterpolation(prevInitialInterpolation) 42 | if (!initialInterpolator.isSmooth) 43 | prevInitialInterpolation = initialInterpolation 44 | zoomingLastTick = zooming 45 | } 46 | 47 | private fun tickScroll(scrollTiers: Int, lastFrameDuration: Double) { 48 | if (scrollTiers > lastScrollTier) 49 | resetting = false 50 | 51 | var targetZoom = 52 | if (maxScrollTiers() > 0) 53 | scrollTiers.toDouble() / Zoomify.maxScrollTiers 54 | else 0.0 55 | if (linearLikeSteps()) { 56 | val curvature = 0.3 57 | val exp = 1 / (1 - curvature) 58 | targetZoom = 2 * (targetZoom.pow(exp) / (targetZoom.pow(exp) + (2 - targetZoom).pow(exp))) 59 | } 60 | 61 | prevScrollInterpolation = scrollInterpolation 62 | scrollInterpolation = scrollInterpolator.tickInterpolation(targetZoom, scrollInterpolation, lastFrameDuration) 63 | prevScrollInterpolation = scrollInterpolator.modifyPrevInterpolation(prevScrollInterpolation) 64 | if (!initialInterpolator.isSmooth) 65 | prevInitialInterpolation = initialInterpolation 66 | lastScrollTier = scrollTiers 67 | } 68 | 69 | fun getZoomDivisor(tickDelta: Float = 1f): Double { 70 | val initialMultiplier = getInitialZoomMultiplier(tickDelta) 71 | val scrollDivisor = getScrollZoomDivisor(tickDelta) 72 | 73 | return (1 / initialMultiplier + scrollDivisor).also { 74 | if (initialInterpolation == 0.0 && scrollInterpolation == 0.0) resetting = false 75 | if (!resetting) resetMultiplier = 1 / it 76 | } 77 | } 78 | 79 | private fun getInitialZoomMultiplier(tickDelta: Float): Double { 80 | return Mth.lerp( 81 | if (initialInterpolator.isSmooth) initialInterpolator.modifyInterpolation( 82 | Mth.lerp( 83 | tickDelta.toDouble(), 84 | prevInitialInterpolation, 85 | initialInterpolation 86 | ) 87 | ) else initialInterpolation, 88 | 1.0, 89 | if (!resetting) 1 / initialZoom().toDouble() else resetMultiplier 90 | ) 91 | } 92 | 93 | private fun getScrollZoomDivisor(tickDelta: Float): Double { 94 | return Mth.lerp( 95 | if (scrollInterpolator.isSmooth) scrollInterpolator.modifyInterpolation( 96 | Mth.lerp( 97 | tickDelta.toDouble(), 98 | prevScrollInterpolation, 99 | scrollInterpolation 100 | ) 101 | ) 102 | else scrollInterpolation, 103 | 0.0, 104 | Zoomify.maxScrollTiers * (scrollZoomAmount() * 3.0) 105 | ).let { if (resetting) 0.0 else it } 106 | } 107 | 108 | fun reset() { 109 | if (!resetting && scrollInterpolation > 0.0) { 110 | resetting = true 111 | scrollInterpolation = 0.0 112 | prevScrollInterpolation = 0.0 113 | } 114 | } 115 | 116 | fun setToZero(initial: Boolean = true, scroll: Boolean = true) { 117 | if (initial) { 118 | initialInterpolation = 0.0 119 | prevInitialInterpolation = 0.0 120 | zoomingLastTick = false 121 | } 122 | if (scroll) { 123 | scrollInterpolation = 0.0 124 | prevScrollInterpolation = 0.0 125 | lastScrollTier = 0 126 | } 127 | resetting = false 128 | } 129 | 130 | fun skipInitial() { 131 | initialInterpolation = 1.0 132 | prevInitialInterpolation = 1.0 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/en_gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "Zoom", 3 | "zoomify.key.zoom.in": "Scroll Zoom In", 4 | "zoomify.key.zoom.out": "Scroll Zoom Out", 5 | "zoomify.key.category": "Zoomify", 6 | 7 | 8 | "zoomify.gui.title": "Zoomify", 9 | 10 | "yacl3.config.zoomify.category.behaviour": "Behaviour", 11 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "Initial Zoom", 12 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "The zoom level when you first press the keybind.", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "Zoom In Time", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "How many seconds to complete zooming in.", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "Zoom Out Time", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "How many seconds to complete zooming out.", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "Zoom In Transition", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "The transition type used when zooming in.", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "Zoom Out Transition", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "The transition type used when zooming out.", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "Affect Hand FOV", 22 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "When zooming, keep the FOV of the hand normal while the world view zooms in.", 23 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "Remember Zoom Steps", 24 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "Keep scroll zoom amount between zooming in and out.", 25 | 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "Scrolling", 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "Enable Scroll Zoom", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "Allow you to zoom in and out more with scroll wheel.", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "Scroll Zoom Amount", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "The additional divisor of the FOV per scroll step.", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "Scroll Zoom Smoothness", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "How smooth/how long it takes to scroll-zoom.", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "Smooth Transition", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "Makes scrolling in and out smooth.", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "Linear-like Steps", 36 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "Applies a curve to the increments to make them feel more linear.", 37 | 38 | "yacl3.config.zoomify.category.controls": "Controls", 39 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "Zoom Key Behaviour", 40 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "The behaviour of the zoom key.", 41 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling": "Keybind Scrolling", 42 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling.description.1": "Use Minecraft keybinds to control scroll zoom instead of the scroll wheel.", 43 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "Relative Sensitivity", 44 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "Controls the sensitivity based on the zoom level.", 45 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "Relative View Bobbing", 46 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "Make view-bobbing less jarring when zoomed in.", 47 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "Cinematic Camera", 48 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "How much the camera glides once moved.", 49 | 50 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "Spyglass", 51 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "Zoom Behaviour", 52 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "How Zoomify interacts with spyglass zoom.", 53 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "Overlay Visibility", 54 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "When to show the spyglass overlay.", 55 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "Sound Behaviour", 56 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "When to play the zoom in and out sounds from the spyglass.", 57 | 58 | "yacl3.config.zoomify.category.secondary": "Secondary Zoom", 59 | "yacl3.config.zoomify.category.secondary.root.label.infoLabel": "Completely separate from all other settings of Zoomify. Secondary zoom is toggle-only with a separate keybinding in controls.", 60 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount": "Zoom Amount", 61 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount.description.1": "How many times to divide the current FOV once transition completes.", 62 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime": "Zoom In Time", 63 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime.description.1": "How many seconds to complete zooming in.", 64 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime": "Zoom Out Time", 65 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime.description.1": "How many seconds to complete zooming out.", 66 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom": "Hide HUD On Zoom", 67 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom.description.1": "Stops rendering the HUD whilst zooming in, much like pressing F1.", 68 | 69 | 70 | "yacl3.config.zoomify.category.misc": "Misc", 71 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting": "Unbind Conflicting Keybind", 72 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button": "Unbind", 73 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1": "Removes the keybind that has the same key as Zoomify zoom key.", 74 | "yacl3.config.zoomify.category.misc.group.presets": "Presets", 75 | "zoomify.gui.preset.default": "Default", 76 | "zoomify.gui.preset.optifine": "OptiFine", 77 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 78 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "Apply Preset", 79 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "Makes Zoomify behave like %s.", 80 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "Pressing the button will immediately apply changes!", 81 | 82 | "zoomify.gui.formatter.instant": "Instant", 83 | "zoomify.gui.formatter.seconds": "%s secs", 84 | 85 | "zoomify.transition.instant": "Instant", 86 | "zoomify.transition.linear": "Linear", 87 | "zoomify.transition.ease_in_sine": "Ease In Sine", 88 | "zoomify.transition.ease_out_sine": "Ease Out Sine", 89 | "zoomify.transition.ease_in_out_sine": "Ease In Out Sine", 90 | "zoomify.transition.ease_in_quad": "Ease In Quad", 91 | "zoomify.transition.ease_out_quad": "Ease Out Quad", 92 | "zoomify.transition.ease_in_out_quad": "Ease In Out Quad", 93 | "zoomify.transition.ease_in_cubic": "Ease In Cubic", 94 | "zoomify.transition.ease_out_cubic": "Ease Out Cubic", 95 | "zoomify.transition.ease_in_out_cubic": "Ease In Out Cubic", 96 | "zoomify.transition.ease_in_exp": "Ease In Exponential", 97 | "zoomify.transition.ease_out_exp": "Ease Out Exponential", 98 | "zoomify.transition.ease_in_out_exp": "Ease In Out Exponential", 99 | 100 | 101 | "zoomify.zoom_key_behaviour.hold": "Hold", 102 | "zoomify.zoom_key_behaviour.toggle": "Toggle", 103 | 104 | 105 | "zoomify.spyglass_behaviour.combine": "Combine", 106 | "zoomify.spyglass_behaviour.override": "Override", 107 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "Allow While Holding", 108 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "Allow While Carrying", 109 | 110 | "zoomify.overlay_visibility.never": "Never", 111 | "zoomify.overlay_visibility.holding": "Holding", 112 | "zoomify.overlay_visibility.carrying": "Carrying", 113 | "zoomify.overlay_visibility.always": "Always", 114 | 115 | "zoomify.sound_behaviour.never": "Never", 116 | "zoomify.sound_behaviour.always": "Always", 117 | "zoomify.sound_behaviour.only_spyglass": "Only Holding Spyglass", 118 | "zoomify.sound_behaviour.with_overlay": "Match Overlay", 119 | 120 | "zoomify.toast.conflictingKeybind.title": "Zoomify won't work!", 121 | "zoomify.toast.conflictingKeybind.description": "There is a conflicting keybind preventing the usage of the zoom key. You can unbind it in the %s category of Zoomify settings.", 122 | "zoomify.toast.unbindConflicting.name": "Unbound %s", 123 | "zoomify.toast.unbindConflicting.description": "Zoomify unbound key because it was conflicting with the zoom key.", 124 | 125 | "zoomify.gui.donate": "Support me!" 126 | } 127 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/es_es.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "Zoom", 3 | "zoomify.key.gui": "Abrir interfaz", 4 | "zoomify.key.category": "Zoomify", 5 | 6 | 7 | "yacl3.config.zoomify.title": "Zoomify", 8 | 9 | "yacl3.config.zoomify.category.behaviour": "Comportamiento", 10 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "Zoom inicial", 11 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "El nivel de acercamiento cuando recién presionas la tecla de zoom.", 12 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "Tiempo de entrada", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "Cuántos segundos para que la animación inicial se complete.", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "Tiempo de salida", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "Cuántos segundos para que la animación final se complete.", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "Transición de acercamiento", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "El tipo de transición usada al acercarse.", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "Transición de alejamiento", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "El tipo de transición usada al alejarse.", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "Afectar campo de visión de la mano", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "Cuando se haga zoom, que la mano no se acerque junto con el mundo.", 22 | 23 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "Rueda de scroll", 24 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "Habilitar zoom con scroll", 25 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "Te deja acercarte y alejarte más con la rueda de scroll.", 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "Nivel de zoom por cada scroll", 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "El divisor adicional del campo de visión por cada paso de la rueda de scroll.", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "Fluidez del zoom con scroll", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "Qué tan fluido y qué tanto tarda el zoom por scroll.", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "Transición fluida", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "Hace que el acercamiento y alejamiento por scroll sea fluido.", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "Incrementos pseudo-lineares", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "Aplica una curva a los incrementos para hacerlos sentir mas lineares.", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "Recordar nivel de zoom", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "Mantener el nivel de acercamiento entre zooms.", 36 | 37 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "Catalejo", 38 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "Comportamiento del zoom", 39 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "Cómo Zoomify interactúa con el zoom del catalejo.", 40 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "Visibilidad del overlay", 41 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "Cuándo mostrar el overlay del catalejo.", 42 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "Comportamiento de los FX", 43 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "Cuándo reproducir los sonidos de acercamiento y alejamiento del catalejo.", 44 | 45 | "yacl3.config.zoomify.category.controls": "Controles", 46 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "Comportamiento de la tecla zoom", 47 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "El comportamiento de la tecla zoom.", 48 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "Sensibilidad relativa", 49 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "Controla la sensibilidad de la cámara basada en el nivel de zoom.", 50 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "Balanceo relativo de la cámara", 51 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "Hace que el balanceo de la cámara sea menos incómodo cuando esta está acercada.", 52 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "Cámara cinemática", 53 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "Hacer que la cámara se deslice suavemente al hacer zoom.", 54 | 55 | "yacl3.config.zoomify.category.misc": "Misceláneo", 56 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting": "Desvincular tecla en conflicto", 57 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button": "Desvincular", 58 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1": "Desvincula la(s) tecla(s) que está(n) asignada(s) igual que la tecla de zoom de Zoomify.", 59 | "yacl3.config.zoomify.category.misc.group.presets": "Preajustes", 60 | "zoomify.gui.preset.default": "Default", 61 | "zoomify.gui.preset.optifine": "OptiFine", 62 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 63 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "Aplicar preajuste", 64 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "Hace que Zoomify actúe como %s.", 65 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "Presionar este botón aplicará los cambios de inmediato!", 66 | 67 | 68 | "zoomify.transition.instant": "Instantánea", 69 | "zoomify.transition.linear": "Linear", 70 | "zoomify.transition.ease_in_sine": "Ease In (Seno)", 71 | "zoomify.transition.ease_out_sine": "Ease Out (Seno)", 72 | "zoomify.transition.ease_in_out_sine": "Ease In Out (Seno)", 73 | "zoomify.transition.ease_in_quad": "Ease In (Quad)", 74 | "zoomify.transition.ease_out_quad": "Ease Out (Quad)", 75 | "zoomify.transition.ease_in_out_quad": "Ease In Out (Quad)", 76 | "zoomify.transition.ease_in_cubic": "Ease In (Cúbico)", 77 | "zoomify.transition.ease_out_cubic": "Ease Out (Cúbico)", 78 | "zoomify.transition.ease_in_out_cubic": "Ease In Out (Cúbico)", 79 | "zoomify.transition.ease_in_exp": "Ease In (Exponencial)", 80 | "zoomify.transition.ease_out_exp": "Ease Out (Exponencial)", 81 | "zoomify.transition.ease_in_out_exp": "Ease In Out (Exponencial)", 82 | 83 | 84 | "zoomify.zoom_key_behaviour.hold": "Mantener", 85 | "zoomify.zoom_key_behaviour.toggle": "Alternar", 86 | 87 | 88 | "zoomify.spyglass_behaviour.combine": "Combinar", 89 | "zoomify.spyglass_behaviour.override": "Sobreescribir", 90 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "Estando en una mano", 91 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "Estando en el inv.", 92 | 93 | "zoomify.overlay_visibility.never": "Nunca", 94 | "zoomify.overlay_visibility.holding": "Estando en una mano", 95 | "zoomify.overlay_visibility.carrying": "Estando en el inv.", 96 | "zoomify.overlay_visibility.always": "Siempre", 97 | 98 | "zoomify.sound_behaviour.never": "Nunca", 99 | "zoomify.sound_behaviour.always": "Siempre", 100 | "zoomify.sound_behaviour.only_spyglass": "Con el catalejo en mano", 101 | "zoomify.sound_behaviour.with_overlay": "Coincidir con el overlay", 102 | 103 | "zoomify.toast.conflictingKeybind.title": "Zoomify no funcionará!", 104 | "zoomify.toast.conflictingKeybind.description": "Hay una tecla en conflicto previniendo el uso de la tecla de zoom. Puedes desvincularla en la categoría de %s en los ajustes de Zoomify.", 105 | "zoomify.toast.unbindConflicting.name": "La tecla en conflicto ha sido desvinculada", 106 | "zoomify.toast.unbindConflicting.description": "Zoomify desvinculó la tecla llamada '%s' porque estaba en conflicto con la tecla de zoom.", 107 | 108 | "zoomify.gui.donate": "Apóyame!" 109 | } 110 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/et_ee.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "Suum", 3 | "zoomify.key.gui": "Ava liides", 4 | "zoomify.key.category": "Zoomify", 5 | 6 | 7 | "zoomify.gui.title": "Zoomify", 8 | 9 | "yacl3.config.zoomify.category.behaviour": "Käitumine", 10 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "Esialgne suum", 11 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "Suumitase esialgsel klahvivajutusel.", 12 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "Sissesuumimise aeg", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "Mitu sekundit kulub sissesuumimiseks.", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "Väljasuumimise aeg", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "Mitu sekundit kulub väljasuumimiseks.", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "Sisse suumimise üleminek", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "Sisse suumimisel kasutatav üleminekutüüp.", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "Välja suumimise üleminek", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "Välja suumimisel kasutatav üleminekutüüp.", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "Mõjuta käe vaatevälja", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "Hoia suumimisel käe vaateväli normaalsena, kuni maailmavaade sisse suumib.", 22 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "Jäta suumitasemed meelde", 23 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "Jäta suumitasemete arv enne välja suumimist meelde, et seda arvu järgmisel suumimisel kasutada.", 24 | 25 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "Kerimine", 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "Luba kerimissuum", 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "Võimaldab kerimisrattaga rohkem sisse ja välja suumida.", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "Kerimissuumi kogus", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "Täiendav vaatevälja jagaja kerimisammu kohta.", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "Kerimissuumi sujuvus", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "Kui sujuv/kui kaua kulub aega kerimissuumiks.", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "Sujuv üleminek", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "Muudab sisse-välja kerimise sujuvaks.", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "Lineaarse-sarnased sammud", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "Lisab juurdekasvudele kõveruse, et need tunduksid lineaarsemad.", 36 | 37 | "yacl3.config.zoomify.category.controls": "Juhtnupud", 38 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "Suumiklahvi käitumine", 39 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "Suumiklahvi käitumise valik.", 40 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "Suhteline tundlikkus", 41 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "Juhib suumitasemest sõltuvat tundlikkuse üleminekut.", 42 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "Suhteline vaate hüplemine", 43 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "Muudab vaate hüplemise sissesuumimisel vähem \"teravaks\".", 44 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "Kinemaatiline kaamera", 45 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "Muuda kaamera suumimise ajal sujuvalt liikuma.", 46 | 47 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "Pikksilm", 48 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "Suumi käitumine", 49 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "Kuidas Zoomify käitub pikksilma suumiga.", 50 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "Ülekatte nähtavus", 51 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "Millal kuvada pikksilma ülekatet.", 52 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "Helikäitumine", 53 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "Millal esitada pikksilma sisse-välja suumimise helisid.", 54 | 55 | "yacl3.config.zoomify.category.misc": "Varia", 56 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting": "Tühjenda konfliktse kiirklahvi määrang", 57 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button": "Tühjenda", 58 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1": "Eemaldab kiirklahvi määrangu, millel on Zoomify suumiklahviga sama klahv.", 59 | "yacl3.config.zoomify.category.misc.group.presets": "Eelseadistused", 60 | "zoomify.gui.preset.default": "Vaikimisi", 61 | 62 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "Rakenda eelseadistus", 63 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "Määrab Zoomify käituma nagu %s.", 64 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "Nupu vajutamine rakendab muudatused koheselt!", 65 | 66 | 67 | "zoomify.transition.instant": "Kohene", 68 | "zoomify.transition.linear": "Lineaarne", 69 | "zoomify.transition.ease_in_sine": "Sujuvalt sisse siinus", 70 | "zoomify.transition.ease_out_sine": "Sujuvalt välja siinus", 71 | "zoomify.transition.ease_in_out_sine": "Sujuvalt sisse-välja siinus", 72 | "zoomify.transition.ease_in_quad": "Sujuvalt sisse nelinurk", 73 | "zoomify.transition.ease_out_quad": "Sujuvalt välja nelinurk", 74 | "zoomify.transition.ease_in_out_quad": "Sujuvalt sisse-välja nelinurk", 75 | "zoomify.transition.ease_in_cubic": "Sujuvalt sisse kuubik", 76 | "zoomify.transition.ease_out_cubic": "Sujuvalt välja kuubik", 77 | "zoomify.transition.ease_in_out_cubic": "Sujuvalt sisse-välja kuubik", 78 | "zoomify.transition.ease_in_exp": "Sujuvalt eksponentsiaalselt", 79 | "zoomify.transition.ease_out_exp": "Sujuvalt eksponentsiaalne", 80 | "zoomify.transition.ease_in_out_exp": "Sujuvalt sisse-välja eksponentsiaalselt", 81 | 82 | 83 | "zoomify.zoom_key_behaviour.hold": "Hoia", 84 | "zoomify.zoom_key_behaviour.toggle": "Lülita", 85 | 86 | "zoomify.spyglass_behaviour.combine": "Kombineeri", 87 | "zoomify.spyglass_behaviour.override": "Kirjuta üle", 88 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "Luba käes hoides", 89 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "Luba kaasas kandes", 90 | 91 | "zoomify.overlay_visibility.never": "Mitte kunagi", 92 | "zoomify.overlay_visibility.holding": "Hoides", 93 | "zoomify.overlay_visibility.carrying": "Kaasas kandes", 94 | "zoomify.overlay_visibility.always": "Alati", 95 | 96 | "zoomify.sound_behaviour.never": "Mitte kunagi", 97 | "zoomify.sound_behaviour.always": "Alati", 98 | "zoomify.sound_behaviour.only_spyglass": "Ainult pikksilma hoidmisel", 99 | "zoomify.sound_behaviour.with_overlay": "Sama, mis ülekatte valik", 100 | 101 | "zoomify.toast.conflictingKeybind.title": "Zoomify ei tööta!", 102 | "zoomify.toast.conflictingKeybind.description": "Konfliktne kiirklahvi määrang ei võimalda suumiklahvi kasutada. Sa saad selle tühjendada Zoomify seadete kategoorias %s.", 103 | "zoomify.toast.unbindConflicting.name": "Konfliktne kiirklahv tühjendatud", 104 | "zoomify.toast.unbindConflicting.description": "Zoomify tühjendas klahvi \"%s\" määrangu, kuna see oli suumiklahviga konfliktis." 105 | } 106 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom":"ズーム", 3 | "zoomify.key.zoom.secondary":"第二ズーム", 4 | "zoomify.key.zoom.in":"スクロールズームイン", 5 | "zoomify.key.zoom.out":"スクロールズームアウト", 6 | "zoomify.key.category":"Zoomify", 7 | 8 | "zoomify.gui.title":"Zoomify", 9 | 10 | "yacl3.config.zoomify.category.behaviour":"挙動", 11 | "yacl3.config.zoomify.category.controls":"操作", 12 | "yacl3.config.zoomify.category.secondary":"第二ズーム", 13 | "yacl3.config.zoomify.category.misc":"その他", 14 | 15 | "yacl3.config.zoomify.category.behaviour.group.basic":"基本", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom":"初期ズーム倍率", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1":"最初にキーバインドを押したときのズーム倍率.", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime":"ズームイン時間", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1":"ズームインが完了するまでの秒数.", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime":"ズームアウト時間", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1":"ズームアウトが完了するまでの秒数.", 22 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition":"ズームインの倍率推移", 23 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1":"ズームイン時に使用する倍率推移パターン.", 24 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition":"ズームアウトの倍率推移", 25 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1":"ズームアウト時に使用する倍率推移パターン.", 26 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov":"手の見え方への影響", 27 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1":"無効にした場合,ズーム中も手の見え方を通常のままにする.", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps":"倍率を保持", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1":"スクロールズームの倍率を保持する.", 30 | 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling":"スクロール", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom":"スクロールズーム", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1":"スクロールホイールでズーム倍率を変えられるようにする.", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount":"スクロールズーム量", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1":"スクロール毎の追加視野除数(乗数).", 36 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness":"スクロールズームの滑らかさ", 37 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1":"スクロールズームの滑らかさ/かかる時間.", 38 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps":"直線的な倍率変更", 39 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1":"増分に曲線を適用することで見かけの変化をより直線的にする.", 40 | 41 | "yacl3.config.zoomify.category.behaviour.group.spyglass":"望遠鏡", 42 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour":"ズームの挙動", 43 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1":"Zoomifyと望遠鏡のズームがどのように相互作用するか.", 44 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility":"オーバーレイ表示", 45 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1":"望遠鏡のオーバーレイを表示する状況.", 46 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour":"音の再生", 47 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1":"望遠鏡の音を再生する状況.", 48 | 49 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour":"ズームキーの挙動", 50 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1":"ズームキーの挙動.", 51 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling":"スクロールのキー割り当て", 52 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling.description.1":"Minecraftのキー割り当てを使用してスクロールホイール以外でスクロールズームを操作する.", 53 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity":"相対的な感度", 54 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1":"ズーム倍率に応じてマウス感度を調節する.", 55 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing":"相対的な画面の揺れ", 56 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1":"ズーム中に画面の揺れを小さくする.", 57 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera":"映画的なカメラ", 58 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1":"カメラの動きをどの程度滑らせるか.", 59 | 60 | "yacl3.config.zoomify.category.secondary.root.label.infoLabel":"Zoomifyの他の設定とは完全に独立した設定です.\n第二ズームは切り替えのみのオプションであり,操作設定に個別のキー割り当てがあります.", 61 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount":"ズーム量", 62 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount.description.1":"ズーム完了時に現在の視野を何分割するか.", 63 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime":"ズームイン時間", 64 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime.description.1":"ズームイン完了までにかかる時間.", 65 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime":"ズームアウト時間", 66 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime.description.1":"ズームアウト完了までにかかる時間.", 67 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom":"ズーム中にHUDを隠す", 68 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom.description.1":"ズーム中はF1を押したときのようにHUDを非表示にする.", 69 | 70 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting":"競合しているキー割り当てを解除する", 71 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button":"解除", 72 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1":"Zoomifyのズームキーと同じキーを使用しているキー割り当てを削除する.", 73 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations":"移行を確認", 74 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.button":"確認", 75 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.description.1":"利用可能な自動移行を確認する.", 76 | "yacl3.config.zoomify.category.misc.group.presets":"プリセット", 77 | "zoomify.gui.preset.default":"デフォルト", 78 | "zoomify.gui.preset.optifine":"OptiFine", 79 | "zoomify.gui.preset.ok_zoomer":"OK Zoomer", 80 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button":"プリセットを適用", 81 | "zoomify.gui.preset.toast.title":"プリセット適用", 82 | "zoomify.gui.preset.toast.description":"Zoomifyに%sのプリセットを適用しました.", 83 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description":"Zoomifyで%sの挙動を再現する.", 84 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning":"プリセット適用は即座に行われ,保存していない変更はすべて取り消されます.", 85 | "zoomify.gui.formatter.instant":"即時", 86 | "zoomify.gui.formatter.seconds":"%s 秒", 87 | 88 | "zoomify.transition.instant":"即時", 89 | "zoomify.transition.linear":"線形", 90 | "zoomify.transition.ease_in_sine":"イーズイン(正弦)", 91 | "zoomify.transition.ease_out_sine":"イーズアウト(正弦)", 92 | "zoomify.transition.ease_in_out_sine":"イーズインアウト(正弦)", 93 | "zoomify.transition.ease_in_quad":"イーズイン(二乗)", 94 | "zoomify.transition.ease_out_quad":"イーズアウト(二乗)", 95 | "zoomify.transition.ease_in_out_quad":"イーズインアウト(二乗)", 96 | "zoomify.transition.ease_in_cubic":"イーズイン(三乗)", 97 | "zoomify.transition.ease_out_cubic":"イーズアウト(三乗)", 98 | "zoomify.transition.ease_in_out_cubic":"イーズインアウト(三乗)", 99 | "zoomify.transition.ease_in_exp":"イーズイン(指数)", 100 | "zoomify.transition.ease_out_exp":"イーズアウト(指数)", 101 | "zoomify.transition.ease_in_out_exp":"イーズインアウト(指数)", 102 | 103 | "zoomify.spyglass_behaviour.combine":"組み合わせ", 104 | "zoomify.spyglass_behaviour.override":"上書き", 105 | "zoomify.spyglass_behaviour.only_zoom_while_holding":"手に持っているときのみ許可", 106 | "zoomify.spyglass_behaviour.only_zoom_while_carrying":"所持しているときのみ許可", 107 | 108 | "zoomify.overlay_visibility.never":"しない", 109 | "zoomify.overlay_visibility.holding":"手に持っているとき", 110 | "zoomify.overlay_visibility.carrying":"所持しているとき", 111 | "zoomify.overlay_visibility.always":"常に", 112 | 113 | "zoomify.sound_behaviour.never":"しない", 114 | "zoomify.sound_behaviour.always":"常に", 115 | "zoomify.sound_behaviour.only_spyglass":"望遠鏡を手に持っているとき", 116 | "zoomify.sound_behaviour.with_overlay":"オーバーレイ表示に合わせる", 117 | 118 | "zoomify.zoom_key_behaviour.hold":"固定", 119 | "zoomify.zoom_key_behaviour.toggle":"切り替え", 120 | 121 | "zoomify.toast.conflictingKeybind.title":"Zoomifyは機能しません!", 122 | "zoomify.toast.conflictingKeybind.description":"ズームキーと競合しているキー割り当てがあります.Zoomifyの設定の%sカテゴリで割り当てを解除できます.", 123 | "zoomify.toast.unbindConflicting.name":"競合キー割り当て解除", 124 | "zoomify.toast.unbindConflicting.description":"ズームキーと競合していたキー割り当て「%s」を解除しました.", 125 | 126 | "zoomify.migrate.no_migrations":"利用可能な移行はありません.", 127 | "zoomify.migrate.result.title":"移行完了", 128 | "zoomify.migrate.restart":"再起動が必要です", 129 | "zoomify.migrate.result.restart_game":"再起動", 130 | "zoomify.migrate.available.title":"%sの自動移行が利用可能", 131 | "zoomify.migrate.available.message":"%sの設定を自動的にZoomifyに移行しますか?", 132 | 133 | "zoomify.migrate.okz":"OK Zoomer", 134 | "zoomify.migrate.okz.persistent":"persistent zoom modeには対応していません.", 135 | "zoomify.migrate.okz.vignette":"vignette overlayには対応していません.", 136 | "zoomify.migrate.okz.minZoomDiv":"minimum zoom divisorの設定には対応していません.", 137 | "zoomify.migrate.okz.stepAmt":"scroll step amountの設定には対応していません.", 138 | "zoomify.migrate.okz.linearNotSupported":"linear timingsは自動的に計算されません.", 139 | 140 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom":"滑らかな倍率変更", 141 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1":"スクロールズームを滑らかにする." 142 | } 143 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/pt_br.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "Zoom", 3 | "zoomify.key.gui": "Abrir GUI", 4 | "zoomify.key.category": "Zoomify", 5 | 6 | 7 | "zoomify.gui.title": "Zoomify", 8 | 9 | "yacl3.config.zoomify.category.behaviour": "Comportamento", 10 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "Zoom Inicial", 11 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "O nível de zoom quando você aperta a tecla primeiro.", 12 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "Tempo para aumentar o Zoom", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "Quantos segundos leva para aumentar o Zoom.", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "Tempo para diminuir o Zoom", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "Quantos segundos leva para diminuir o Zoom.", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "Transição de entrada do zoom", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "O tipo de transição usado na entrada do zoom.", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "Transição de saída do zoom", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "O tipo de transição usado na saída do zoom.", 20 | 21 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "Rolagem", 22 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "Habilitar Zoom de Rolagem", 23 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "Permitir você dar zoom com a roda do mouse.", 24 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "Quantidade de Zoom de Rolagem", 25 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "O divisor adicional do FOV por etapa de rolagem.", 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "Suavidade do Zoom de Rolagem", 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "Quão suave/quanto tempo leva para usar o Zoom de Rolagem.", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "Transição suave", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "Faz o zoom aumentar e diminuir suavemente.", 30 | 31 | "yacl3.config.zoomify.category.controls": "Controles", 32 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "Comportamento da telca de Zoom", 33 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "O comportamento da tecla de zoom.", 34 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "Sensibilidade relativa", 35 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "Controla o gradiente da sensibilidade baseado no nível de zoom.", 36 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "Balançado Relativo da Visão", 37 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "Faz o balançar da tela menos duro com o Zoom ativo.", 38 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "Câmera cinemática", 39 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "Faz a câmera se mover suavemente no zoom.", 40 | 41 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "Afetar o Campo de Visão da Mão", 42 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "Com zoom, mantém o campo de visão da mão normal, enquanto o mundo aumenta.", 43 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "Lembrar Etapas de Zoom", 44 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "Mantém a quantia de zoom entre entrada e saída de zoom.", 45 | 46 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "Etapas Quase Lineares", 47 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "Aplica uma curva aos incrementos para eles parecerem lineares.", 48 | 49 | "yacl3.config.zoomify.category.misc.group.presets": "Padronizações", 50 | "zoomify.gui.preset.default": "Padrão", 51 | "zoomify.gui.preset.optifine": "OptiFine", 52 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 53 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "Aplicar Padronização", 54 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "Faz o Zoomify se comportar como %s.", 55 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "Pressionar o botão vai aplicar as mudanças instantaneamente!", 56 | 57 | "zoomify.transition.instant": "Instantâneo", 58 | "zoomify.transition.linear": "Linear", 59 | "zoomify.transition.ease_in_sine": "Entrada Suave Seno", 60 | "zoomify.transition.ease_out_sine": "Saída Suave Seno", 61 | "zoomify.transition.ease_in_out_sine": "Entrada Saída Suave Seno", 62 | "zoomify.transition.ease_in_quad": "Entrada Suave Quadrática", 63 | "zoomify.transition.ease_out_quad": "Saída Suave Quadrática", 64 | "zoomify.transition.ease_in_out_quad": "Entrada Saída Suave Quadrática", 65 | "zoomify.transition.ease_in_cubic": "Entrada Suave Cúbica", 66 | "zoomify.transition.ease_out_cubic": "Saída Suave Cúbica", 67 | "zoomify.transition.ease_in_out_cubic": "Entrada Saída Suave Cúbica", 68 | "zoomify.transition.ease_in_exp": "Entrada Suave Exponencial", 69 | "zoomify.transition.ease_out_exp": "Saída Suave Exponencial", 70 | "zoomify.transition.ease_in_out_exp": "Entrada Saída Suave Exponencial", 71 | 72 | 73 | "zoomify.zoom_key_behaviour.hold": "Segurar", 74 | "zoomify.zoom_key_behaviour.toggle": "Alternar", 75 | 76 | 77 | "zoomify.spyglass_behaviour.combine": "Combinar", 78 | "zoomify.spyglass_behaviour.override": "Substituir", 79 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "Permitir Enquanto Segurando", 80 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "Permitir Enquanto Carregando", 81 | 82 | "zoomify.overlay_visibility.never": "Nunca", 83 | "zoomify.overlay_visibility.holding": "Segurando", 84 | "zoomify.overlay_visibility.carrying": "Carregando", 85 | "zoomify.overlay_visibility.always": "Sempre", 86 | 87 | "zoomify.sound_behaviour.never": "Nunca", 88 | "zoomify.sound_behaviour.always": "Sempre", 89 | "zoomify.sound_behaviour.only_spyglass": "Apenas Segurando Luneta", 90 | "zoomify.sound_behaviour.with_overlay": "Corresponder Sobreposição" 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/tr_tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "Yakınlaştır", 3 | "zoomify.key.gui": "Menüyü Aç", 4 | "zoomify.key.category": "Zoomify", 5 | 6 | 7 | "zoomify.gui.title": "Zoomify", 8 | 9 | "yacl3.config.zoomify.category.behaviour": "Davranış", 10 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "İlk Yakınlaştırma", 11 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "Tuş takımına ilk bastığınızda yakınlaştırma düzeyi", 12 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "Yakınlaştırma Süresi", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "Yakınlaştırmayı tamamlamak için ne kadar sürenin var olduğu", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "Uzaklaştırma Süresi", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "Uzaklaştırmayı tamamlamak için ne kadar sürenin var olduğu", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "Yakınlaştırma Geçişi", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "Yakınlaştırırken kullanılan geçiş türü", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "Uzaklaştırma Geçişi", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "Uzaklaştırırken kullanılan geçiş türü", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "El FOV'unu Etkile", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "Yakınlaştırırken, görüş yaklaşırken elin FOV'unu normal tutun.", 22 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "Yakınlaştırma Adımlarını Hatırla", 23 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "Yakınlaştırma ve uzaklaştırma arasındaki kaydırarak yakınlaştırma miktarını koruyun.", 24 | 25 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "Kaydırma", 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "Kaydırma Yakınlaştırmasını Etkinleştir", 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "Kaydırma tekerleği ile daha fazla yakınlaştırma ve uzaklaştırma yapmanızı sağlar.", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "Kaydırma Yakınlaştırma Miktarı", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "Kaydırma adımı başına FOV'un ek böleni", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "Kaydırma Yakınlaştırma Pürüzsüzlüğü", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "Kaydırmanın-Yakınlaştırmanın ne kadar yumuşak olduğu/ne kadar sürdüğü", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "Yumuşak Geçiş", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "İçeri ve dışarı kaydırmayı pürüzsüz yapar.", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "Doğrusal Benzeri Adımlar", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "Daha doğrusal hissettirmek için artışlara bir eğri uygular.", 36 | 37 | "yacl3.config.zoomify.category.controls": "Kontroller", 38 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "Yakınlaştırma Tuşu Davranışı", 39 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "Yakınlaştırma tuşunun davranışı", 40 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "Bağıl Hassaslık", 41 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "Yakınlaştırma düzeyine göre hassasiyeti kontrol eder.", 42 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "Bağıl Sallanan Görüntü", 43 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "Yakınlaştırıldığında görüntü sallanmasını daha az sarsıcı hale getir.", 44 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "Sinematik Kamera", 45 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "Yakınlaştırıldığında kameranın düzgünce hareket etmesini sağla.", 46 | 47 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "Dürbün", 48 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "Yakınlaştırma Davranışı", 49 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "Zoomify'ın dürbün yakınlaştırma ile nasıl etkileşime geçeceği", 50 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "Kaplama Görünürlüğü", 51 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "Dürbün kaplamasının ne zaman gösterileceği", 52 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "Ses Davranışı", 53 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "Dürbünden gelen yakınlaştırma ve uzaklaştırma seslerinin ne zaman oynatılacağı", 54 | 55 | "yacl3.config.zoomify.category.misc.group.presets": "Ön Ayarlar", 56 | "zoomify.gui.preset.default": "Varsayılan", 57 | "zoomify.gui.preset.optifine": "OptiFine", 58 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 59 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "Ön Ayarı Uygula", 60 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "Zoomify'ın %s gibi davranmasını sağlar.", 61 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "Düğmeye basmak, değişiklikleri hemen uygulayacaktır!", 62 | 63 | 64 | "zoomify.transition.instant": "Anında", 65 | "zoomify.transition.linear": "Doğrusal", 66 | "zoomify.transition.ease_in_sine": "Kademeli Sinüs Girişi", 67 | "zoomify.transition.ease_out_sine": "Kademeli Sinüs Çıkışı", 68 | "zoomify.transition.ease_in_out_sine": "Kademeli Sinüs Giriş Çıkışı", 69 | "zoomify.transition.ease_in_quad": "Kademeli Dörtlü Giriş", 70 | "zoomify.transition.ease_out_quad": "Kademeli Dörtlü Çıkış", 71 | "zoomify.transition.ease_in_out_quad": "Kademeli Dörtlü Giriş Çıkışı", 72 | "zoomify.transition.ease_in_cubic": "Kademeli Kübik Girişi", 73 | "zoomify.transition.ease_out_cubic": "Kademeli Kübik Çıkışı", 74 | "zoomify.transition.ease_in_out_cubic": "Kademeli Kübik Giriş Çıkışı", 75 | "zoomify.transition.ease_in_exp": "Kademeli Üstel Giriş", 76 | "zoomify.transition.ease_out_exp": "Kademeli Üstel Çıkış", 77 | "zoomify.transition.ease_in_out_exp": "Kademeli Üstel Giriş Çıkışı", 78 | 79 | 80 | "zoomify.zoom_key_behaviour.hold": "Basılı Tut", 81 | "zoomify.zoom_key_behaviour.toggle": "Aç/Kapa", 82 | 83 | 84 | "zoomify.spyglass_behaviour.combine": "Birleştir", 85 | "zoomify.spyglass_behaviour.override": "Geçersiz Kıl", 86 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "Basılı Tutarken İzin Ver", 87 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "Taşırken İzin Ver", 88 | 89 | "zoomify.overlay_visibility.never": "Asla", 90 | "zoomify.overlay_visibility.holding": "Basılı Tutma", 91 | "zoomify.overlay_visibility.carrying": "Taşıma", 92 | "zoomify.overlay_visibility.always": "Her Zaman", 93 | 94 | "zoomify.sound_behaviour.never": "Asla", 95 | "zoomify.sound_behaviour.always": "Her Zaman", 96 | "zoomify.sound_behaviour.only_spyglass": "Sadece Dürbün Tutarken", 97 | "zoomify.sound_behaviour.with_overlay": "Kaplamaya Uy" 98 | } 99 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "放大(按住)", 3 | "zoomify.key.zoom_toggle": "放大(切换)", 4 | "zoomify.key.zoom.secondary": "二次缩放", 5 | "zoomify.key.zoom.in": "滚动放大", 6 | "zoomify.key.zoom.out": "滚动缩小", 7 | "zoomify.key.category": "缩放(Zoomify)", 8 | 9 | 10 | "yacl3.config.zoomify.title": "缩放(Zoomify)", 11 | 12 | "yacl3.config.zoomify.category.behaviour": "行为", 13 | "yacl3.config.zoomify.category.behaviour.group.basic": "基本", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "初始缩放", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "首次按下绑定键时的缩放级别。", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "放大时间", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "多少秒完成放大。", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "缩小时间", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "多少秒完成缩小。", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "放大过渡", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "放大时使用的过渡类型。", 22 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "缩小过度", 23 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "缩小时使用的过渡类型。", 24 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "影响手部FOV值", 25 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "缩放时,世界视图放大且保持手部的FOV值正常。", 26 | 27 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "滚动", 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "启用滚动缩放", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "允许您使用滚轮放大或缩小一些。", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "滚动缩放量", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "每个滚动步骤FOV的附加除数。", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "滚动缩放平滑度", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "滚动缩放的平滑度/需要多长时间。", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "平滑过渡", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "使滚入和滚出更平滑。", 36 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "类似线性步骤", 37 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "将曲线应用于增量以让他们感觉起来更像线性。", 38 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "记住缩放步骤", 39 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "在放大和缩小之间保持滚动缩放量。", 40 | 41 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "望远镜", 42 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "缩放行为", 43 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "Zoomify如何与望远镜缩放相互影响。", 44 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "覆盖可视", 45 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "什么时候显示望远镜覆盖层。", 46 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "声音行为", 47 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "什么时候播放望远镜放大和缩小时的声音。", 48 | 49 | "yacl3.config.zoomify.category.controls": "控制", 50 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "缩放键行为", 51 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "按下缩放键时的行为。", 52 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling": "滚动缩放键绑定", 53 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling.description.1": "使用Minecraft键绑定来控制滚动缩放缩放而不是鼠标滚轮。", 54 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "相对灵敏度", 55 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "根据缩放级别控制灵敏度的梯度。", 56 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "相对视线晃动", 57 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "在放大时使视线晃动较为协调。", 58 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "电影摄像机", 59 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "使相机在缩放时平滑移动。", 60 | 61 | "yacl3.config.zoomify.category.secondary": "二次缩放", 62 | "yacl3.config.zoomify.category.secondary.root.label.infoLabel": "与Zoomify的其他所有设置完全分开。二次缩放仅在控制中使用单独的键绑定进行切换。", 63 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount": "缩放量", 64 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount.description.1": "一旦过渡完成,将当前的FOV划分多少倍。", 65 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime": "放大时间", 66 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime.description.1": "多少秒完成放大。", 67 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime": "缩小时间", 68 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime.description.1": "多少秒完成缩小", 69 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom": "在缩放时隐藏HUD", 70 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom.description.1": "放大时停止渲染HUD,就像按F1。", 71 | 72 | "yacl3.config.zoomify.category.misc": "杂项", 73 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting": "解除冲突键位", 74 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button": "解除", 75 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1": "移除与Zoomify缩放键具有相同键位的键绑定。", 76 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations": "检查迁移", 77 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.button": "检查", 78 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.description.1": "检查可用的自动迁移。", 79 | "yacl3.config.zoomify.category.misc.group.presets": "预设", 80 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "应用预设将立即应用更改并撤消任何未做出决定的更改。", 81 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "应用预设", 82 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "使Zoomify的行为像%s。", 83 | 84 | "zoomify.gui.preset.default": "默认", 85 | "zoomify.gui.preset.optifine": "OptiFine", 86 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 87 | "zoomify.gui.preset.toast.title": "应用预设", 88 | "zoomify.gui.preset.toast.description": "Zoomify应用了%s预设。", 89 | 90 | "zoomify.gui.formatter.instant": "立即", 91 | "zoomify.gui.formatter.seconds": "%s秒", 92 | 93 | "zoomify.transition.instant": "立即", 94 | "zoomify.transition.linear": "直线", 95 | "zoomify.transition.ease_in_sine": "正弦缓入", 96 | "zoomify.transition.ease_out_sine": "正弦缓出", 97 | "zoomify.transition.ease_in_out_sine": "正弦缓出入", 98 | "zoomify.transition.ease_in_quad": "四方缓入", 99 | "zoomify.transition.ease_out_quad": "四方缓出", 100 | "zoomify.transition.ease_in_out_quad": "四方缓出入", 101 | "zoomify.transition.ease_in_cubic": "立方缓入", 102 | "zoomify.transition.ease_out_cubic": "立方缓出", 103 | "zoomify.transition.ease_in_out_cubic": "立方缓出入", 104 | "zoomify.transition.ease_in_exp": "指数缓入", 105 | "zoomify.transition.ease_out_exp": "指数缓出", 106 | "zoomify.transition.ease_in_out_exp": "指数缓出入", 107 | 108 | 109 | "zoomify.zoom_key_behaviour.hold": "开始放大", 110 | "zoomify.zoom_key_behaviour.toggle": "切换放大缩小", 111 | 112 | 113 | "zoomify.spyglass_behaviour.combine": "合并", 114 | "zoomify.spyglass_behaviour.override": "覆盖", 115 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "开镜时启用", 116 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "携带时启用", 117 | 118 | "zoomify.overlay_visibility.never": "从不", 119 | "zoomify.overlay_visibility.holding": "按住时", 120 | "zoomify.overlay_visibility.carrying": "携带时", 121 | "zoomify.overlay_visibility.always": "总是", 122 | 123 | "zoomify.sound_behaviour.never": "从不", 124 | "zoomify.sound_behaviour.always": "总是", 125 | "zoomify.sound_behaviour.only_spyglass": "只有望远镜开镜时", 126 | "zoomify.sound_behaviour.with_overlay": "匹配覆盖(只有按望远镜时)", 127 | 128 | "zoomify.toast.conflictingKeybind.title": "Zoomify不起作用!", 129 | "zoomify.toast.conflictingKeybind.description": "存在冲突的按键绑定,阻止了缩放键起作用。你可以在Zoomify设置的%s类别中取消绑定。", 130 | "zoomify.toast.unbindConflicting.name": "解除冲突键位", 131 | "zoomify.toast.unbindConflicting.description": "Zoomify解除绑定键‘%s’因为它与缩放键冲突。", 132 | 133 | 134 | "zoomify.migrate.no_migrations": "没有可用的迁移。", 135 | "zoomify.migrate.result.title": "迁移完成", 136 | "zoomify.migrate.restart": "需要重启", 137 | "zoomify.migrate.result.restart_game": "重启游戏", 138 | "zoomify.migrate.available.title": "%s的自动迁移可用", 139 | "zoomify.migrate.available.message": "你想要自动将你的%s配置迁移到Zoomify吗?", 140 | 141 | "zoomify.migrate.okz": "OK Zoomer", 142 | "zoomify.migrate.okz.persistent": "不支持持续缩放模式。", 143 | "zoomify.migrate.okz.vignette": "不支持Vignette overlay。", 144 | "zoomify.migrate.okz.minZoomDiv": "不支持设置最小放大倍数。", 145 | "zoomify.migrate.okz.stepAmt": "不支持设置scroll step amount。", 146 | "zoomify.migrate.okz.linearNotSupported": "无法自动计算线性时序。" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "zoomify.key.zoom": "縮放", 3 | "zoomify.key.zoom.secondary": "次要縮放", 4 | "zoomify.key.zoom.in": "向上滾動放大", 5 | "zoomify.key.zoom.out": "向下滾動縮小", 6 | "zoomify.key.category": "Zoomify", 7 | 8 | 9 | "zoomify.gui.title": "Zoomify", 10 | 11 | "yacl3.config.zoomify.category.behaviour": "行為", 12 | "yacl3.config.zoomify.category.behaviour.group.basic": "基本", 13 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom": "初始化縮放大小", 14 | "yacl3.config.zoomify.category.behaviour.group.basic.option.initialZoom.description.1": "按下快捷鍵時的縮放等級。", 15 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime": "放大時間", 16 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTime.description.1": "要多少秒完成放大。", 17 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime": "縮小時間", 18 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTime.description.1": "要多少秒完成縮小。", 19 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition": "放大過渡效果", 20 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomInTransition.description.1": "當放大時使用的過渡效果類型。", 21 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition": "縮小過渡效果", 22 | "yacl3.config.zoomify.category.behaviour.group.basic.option.zoomOutTransition.description.1": "當縮小時使用的過渡效果類型。", 23 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov": "影響手部視角", 24 | "yacl3.config.zoomify.category.behaviour.group.basic.option.affectHandFov.description.1": "當縮放時,在世界視角被縮放時保持手部視角正常。", 25 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps": "記住縮放步驟量", 26 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.retainZoomSteps.description.1": "在放大縮小時保持滾動縮放量。", 27 | 28 | "yacl3.config.zoomify.category.behaviour.group.scrolling": "滾動", 29 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom": "啟用滾動縮放", 30 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoom.description.1": "允許使用滾輪進行更多縮放動作。", 31 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount": "滾動縮放量", 32 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomAmount.description.1": "每次滾動時的視角額外除數。", 33 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness": "滾動縮放平滑度", 34 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.scrollZoomSmoothness.description.1": "滾動縮放時所需要的時間與平滑度。", 35 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom": "平滑過渡效果", 36 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.smoothScrollZoom.description.1": "讓滾動縮放更加的平滑。", 37 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps": "類線性步驟", 38 | "yacl3.config.zoomify.category.behaviour.group.scrolling.option.linearLikeSteps.description.1": "對增量套用曲線,使其感覺更加線性。", 39 | 40 | "yacl3.config.zoomify.category.controls": "控制", 41 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour": "縮放鍵行為", 42 | "yacl3.config.zoomify.category.controls.root.option.zoomKeyBehaviour.description.1": "按下縮放鍵時的行為。", 43 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling": "綁定鍵滾動", 44 | "yacl3.config.zoomify.category.controls.root.option._keybindScrolling.description.1": "使用 Minecraft 綁定鍵來控制滾動縮放而不是用滾輪。", 45 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity": "相對敏感度", 46 | "yacl3.config.zoomify.category.controls.root.option.relativeSensitivity.description.1": "根據縮放等級控制敏感度。", 47 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing": "相對視角晃動", 48 | "yacl3.config.zoomify.category.controls.root.option.relativeViewBobbing.description.1": "使放大時視角晃動更少。", 49 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera": "劇院鏡頭", 50 | "yacl3.config.zoomify.category.controls.root.option.cinematicCamera.description.1": "鏡頭滑動的速度。", 51 | 52 | "yacl3.config.zoomify.category.behaviour.group.spyglass": "望遠鏡", 53 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour": "縮放行為", 54 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassBehaviour.description.1": "Zoomify 的縮放效果,要如何與望遠鏡互動。", 55 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility": "重疊可見度", 56 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassOverlayVisibility.description.1": "何時顯示望遠鏡的重疊。", 57 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour": "音效行為", 58 | "yacl3.config.zoomify.category.behaviour.group.spyglass.option.spyglassSoundBehaviour.description.1": "何時播放來自望遠鏡的放大縮小聲音。", 59 | 60 | "yacl3.config.zoomify.category.secondary": "次要縮放", 61 | "yacl3.config.zoomify.category.secondary.root.label.infoLabel": "完全與 Zoomify 的所有其他設定分離。次要縮放僅具有切換功能,使用單獨的綁定鍵來控制。", 62 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount": "縮放量", 63 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomAmount.description.1": "完成過渡後的視角除數次數。", 64 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime": "放大時間", 65 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomInTime.description.1": "要多少秒完成放大。", 66 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime": "縮小時間", 67 | "yacl3.config.zoomify.category.secondary.root.option.secondaryZoomOutTime.description.1": "要多少秒完成縮小。", 68 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom": "縮放時隱藏 HUD", 69 | "yacl3.config.zoomify.category.secondary.root.option.secondaryHideHUDOnZoom.description.1": "在縮放時停止繪製 HUD,就像是按下 F1 鍵時一樣。", 70 | 71 | "yacl3.config.zoomify.category.misc": "其他", 72 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting": "解除有衝突的按鍵綁定", 73 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.button": "解除綁定", 74 | "yacl3.config.zoomify.category.misc.root.option.unbindConflicting.description.1": "移除和 Zoomify 縮放鍵相同的按鍵綁定。", 75 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations": "檢查轉移", 76 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.button": "檢查", 77 | "yacl3.config.zoomify.category.misc.root.option.checkMigrations.description.1": "檢查是否有可用的自動轉移。", 78 | "yacl3.config.zoomify.category.misc.group.presets": "預設值", 79 | "zoomify.gui.preset.default": "預設", 80 | "zoomify.gui.preset.optifine": "OptiFine", 81 | "zoomify.gui.preset.ok_zoomer": "OK Zoomer", 82 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.button": "套用預設值", 83 | "zoomify.gui.preset.toast.title": "已套用預設值", 84 | "zoomify.gui.preset.toast.description": "Zoomify 已套用 %s 預設值。", 85 | "yacl3.config.zoomify.category.misc.group.presets.presetBtn.description": "讓 Zoomify 行為類似於 %s。", 86 | "yacl3.config.zoomify.category.misc.group.presets.label.applyWarning": "套用預設值將會立即套用變更並撤銷任何擱置中的變更。", 87 | 88 | "zoomify.gui.formatter.instant": "即時", 89 | "zoomify.gui.formatter.seconds": "%s 秒", 90 | 91 | "zoomify.transition.instant": "即時", 92 | "zoomify.transition.linear": "線性", 93 | "zoomify.transition.ease_in_sine": "正弦緩入", 94 | "zoomify.transition.ease_out_sine": "正弦緩出", 95 | "zoomify.transition.ease_in_out_sine": "正弦緩入緩出", 96 | "zoomify.transition.ease_in_quad": "二次方緩入", 97 | "zoomify.transition.ease_out_quad": "二次方緩出", 98 | "zoomify.transition.ease_in_out_quad": "二次方緩入緩出", 99 | "zoomify.transition.ease_in_cubic": "三次方緩入", 100 | "zoomify.transition.ease_out_cubic": "三次方緩出", 101 | "zoomify.transition.ease_in_out_cubic": "三次方緩入緩出", 102 | "zoomify.transition.ease_in_exp": "指數緩入", 103 | "zoomify.transition.ease_out_exp": "指數緩出", 104 | "zoomify.transition.ease_in_out_exp": "指數緩入緩出", 105 | 106 | 107 | "zoomify.zoom_key_behaviour.hold": "按住", 108 | "zoomify.zoom_key_behaviour.toggle": "切換", 109 | 110 | 111 | "zoomify.spyglass_behaviour.combine": "組合", 112 | "zoomify.spyglass_behaviour.override": "覆蓋", 113 | "zoomify.spyglass_behaviour.only_zoom_while_holding": "當按住時", 114 | "zoomify.spyglass_behaviour.only_zoom_while_carrying": "當手持時", 115 | 116 | "zoomify.overlay_visibility.never": "永不", 117 | "zoomify.overlay_visibility.holding": "按住時", 118 | "zoomify.overlay_visibility.carrying": "手持時", 119 | "zoomify.overlay_visibility.always": "總是", 120 | 121 | "zoomify.sound_behaviour.never": "永不", 122 | "zoomify.sound_behaviour.always": "總是", 123 | "zoomify.sound_behaviour.only_spyglass": "僅在持握望遠鏡時", 124 | "zoomify.sound_behaviour.with_overlay": "相符重疊", 125 | 126 | "zoomify.toast.conflictingKeybind.title": "Zoomify 無法運作!", 127 | "zoomify.toast.conflictingKeybind.description": "有按鍵衝突阻止使用縮放按鍵。你可以在 Zoomify 設定中的 %s 類別將衝突的按鍵解除綁定。", 128 | "zoomify.toast.unbindConflicting.name": "解除有衝突的按鍵綁定", 129 | "zoomify.toast.unbindConflicting.description": "Zoomify 解除了名為「%s」的按鍵,因為它與縮放鍵衝突。", 130 | 131 | 132 | "zoomify.migrate.no_migrations": "沒有可用的轉移。", 133 | "zoomify.migrate.result.title": "轉移完成", 134 | "zoomify.migrate.restart": "需要重啟", 135 | "zoomify.migrate.result.restart_game": "重啟遊戲", 136 | "zoomify.migrate.available.title": "自動轉移「%s」的設定可用", 137 | "zoomify.migrate.available.message": "你想要自動轉移你的 %s 設定到 Zoomify?", 138 | 139 | "zoomify.migrate.okz": "OK Zoomer", 140 | "zoomify.migrate.okz.persistent": "不支援永續性縮放模式。", 141 | "zoomify.migrate.okz.vignette": "不支援暈影重疊。", 142 | "zoomify.migrate.okz.minZoomDiv": "不支援設定最小縮放除數。", 143 | "zoomify.migrate.okz.stepAmt": "不支援設定滾輪步驟量。", 144 | "zoomify.migrate.okz.linearNotSupported": "線性時間無法被自動計算。" 145 | } 146 | -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/textures/demo/third-person-hud.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/src/main/resources/assets/zoomify/textures/demo/third-person-hud.webp -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/textures/demo/third-person-view.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/src/main/resources/assets/zoomify/textures/demo/third-person-view.webp -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/textures/demo/zoom-hand.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/src/main/resources/assets/zoomify/textures/demo/zoom-hand.webp -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/textures/demo/zoom-world.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/src/main/resources/assets/zoomify/textures/demo/zoom-world.webp -------------------------------------------------------------------------------- /src/main/resources/assets/zoomify/zoomify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isXander/Zoomify/80337e5b6690d47b10a635acd4db140e49eddc19/src/main/resources/assets/zoomify/zoomify.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${id}", 4 | "version": "${version}", 5 | "name": "${name}", 6 | "description": "${description}", 7 | "authors": [ 8 | "isXander" 9 | ], 10 | "contact": { 11 | "homepage": "https://isxander.dev", 12 | "issues": "https://github.com/isXander/Zoomify/issues", 13 | "sources": "https://github.com/isXander/Zoomify" 14 | }, 15 | "icon": "assets/zoomify/zoomify.png", 16 | "license": "LGPLv3", 17 | "environment": "client", 18 | "entrypoints": { 19 | "client": [ 20 | { 21 | "adapter": "kotlin", 22 | "value": "dev.isxander.zoomify.Zoomify" 23 | } 24 | ], 25 | "preLaunch": [ 26 | "com.llamalad7.mixinextras.MixinExtrasBootstrap::init" 27 | ], 28 | "modmenu": [ 29 | { 30 | "adapter": "kotlin", 31 | "value": "dev.isxander.zoomify.integrations.ModMenuIntegration" 32 | } 33 | ], 34 | "controlify": [ 35 | { 36 | "adapter": "kotlin", 37 | "value": "dev.isxander.zoomify.integrations.ControlifyIntegration" 38 | } 39 | ] 40 | }, 41 | "mixins": [ 42 | "zoomify.mixins.json" 43 | ], 44 | "depends": { 45 | "fabric-api": "*", 46 | "fabricloader": ">=0.14.22", 47 | "fabric-language-kotlin": ">=1.11.0+kotlin.2.0.0", 48 | "minecraft": "${mc}", 49 | "java": ">=17", 50 | "yet_another_config_lib_v3": ">=3.5.0" 51 | }, 52 | "suggests": { 53 | "modmenu": "*" 54 | }, 55 | "breaks": { 56 | "optifabric": "*", 57 | "controlify": "<2.0.0-beta.9" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/zoomify.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.isxander.zoomify.mixins", 4 | "compatibilityLevel": "JAVA_17", 5 | "injectors": { 6 | "defaultRequire": 1 7 | }, 8 | "client": [ 9 | "hooks.MinecraftClientMixin", 10 | "spyglass.AbstractClientPlayerEntityMixin", 11 | "spyglass.InGameHudMixin", 12 | "spyglass.SpyglassItemMixin", 13 | "zoom.GameRendererMixin", 14 | "zoom.MouseMixin", 15 | "zoom.secondary.GameRendererMixin" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /stonecutter.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("dev.kikugie.stonecutter") 3 | } 4 | stonecutter active "1.21.3" /* [SC] DO NOT EDIT */ 5 | 6 | stonecutter.configureEach { 7 | fun String.propDefined() = project.findProperty(this)?.toString()?.isNotBlank() ?: false 8 | 9 | consts(listOf( 10 | "mod-menu" to "deps.modMenu".propDefined() 11 | )) 12 | } 13 | 14 | stonecutter registerChiseled tasks.register("buildAllVersions", stonecutter.chiseled) { 15 | group = "mod" 16 | ofTask("build") 17 | } 18 | 19 | stonecutter registerChiseled tasks.register("releaseAllVersions", stonecutter.chiseled) { 20 | group = "mod" 21 | ofTask("releaseMod") 22 | } 23 | -------------------------------------------------------------------------------- /versions/1.20.1/gradle.properties: -------------------------------------------------------------------------------- 1 | mcVersion=1.20.1 2 | 3 | deps.parchment=1.20.1:2023.09.03 4 | deps.fabricApi=0.92.0+1.20.1 5 | deps.yacl=3.5.0+1.20.1-fabric 6 | deps.modMenu=7.2.2 7 | deps.controlify=2.0.0-beta.12+1.20.1-fabric 8 | 9 | java.version=17 10 | 11 | fmj.mcDep=>=1.20 <=1.20.1 12 | 13 | pub.stableMC=1.20, 1.20.1 14 | -------------------------------------------------------------------------------- /versions/1.20.4/gradle.properties: -------------------------------------------------------------------------------- 1 | mcVersion=1.20.4 2 | 3 | deps.parchment=1.20.4:2024.04.14 4 | deps.fabricApi=0.96.11+1.20.4 5 | deps.yacl=3.5.0+1.20.4-fabric 6 | deps.modMenu=9.0.0 7 | deps.controlify=2.0.0-beta.12+1.20.4-fabric 8 | 9 | java.version=17 10 | 11 | fmj.mcDep=>=1.20.3 <=1.20.4 12 | 13 | pub.stableMC=1.20.3,1.20.4 14 | -------------------------------------------------------------------------------- /versions/1.20.6/gradle.properties: -------------------------------------------------------------------------------- 1 | mcVersion=1.20.6 2 | 3 | deps.parchment=1.20.6:2024.05.01 4 | deps.fabricApi=0.100.0+1.20.6 5 | deps.yacl=3.5.0+1.20.6-fabric 6 | deps.modMenu=10.0.0-beta.1 7 | deps.controlify=2.0.0-beta.12+1.20.6-fabric 8 | 9 | java.version=21 10 | 11 | fmj.mcDep=~1.20.5 12 | 13 | pub.modrinthMC=1.20.5,1.20.6 14 | pub.curseMC=1.20.5,1.20.6 15 | -------------------------------------------------------------------------------- /versions/1.21.1/gradle.properties: -------------------------------------------------------------------------------- 1 | mcVersion=1.21.1 2 | 3 | deps.parchment=1.21:2024.07.28 4 | deps.fabricApi=0.104.0+1.21.1 5 | deps.yacl=3.5.0+1.21-fabric 6 | deps.modMenu=11.0.2 7 | deps.controlify=2.0.0-beta.12+1.21-fabric 8 | 9 | java.version=21 10 | 11 | fmj.mcDep=~1.21 <1.21.2 12 | 13 | pub.modrinthMC=1.21,1.21.1 14 | pub.curseMC=1.21,1.21.1 15 | -------------------------------------------------------------------------------- /versions/1.21.3/gradle.properties: -------------------------------------------------------------------------------- 1 | mcVersion=1.21.3 2 | 3 | #deps.parchment=1.21:2024.07.28 4 | deps.fabricApi=0.106.1+1.21.3 5 | deps.yacl=3.6.1+1.21.2-fabric 6 | deps.modMenu=12.0.0-beta.1 7 | deps.controlify=2.0.0-beta.18+1.21.2-fabric 8 | 9 | java.version=21 10 | 11 | fmj.mcDep=~1.21.2 12 | 13 | pub.modrinthMC=1.21.2,1.21.3 14 | pub.curseMC=1.21.2,1.21.3 15 | --------------------------------------------------------------------------------