├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── build.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
├── api
└── java
│ └── yalter
│ └── mousetweaks
│ └── api
│ └── IMTModGuiContainer2.java
└── main
├── java
└── github
│ └── kasuminova
│ └── ae2ctl
│ ├── AE2CTLegacy.java
│ ├── client
│ ├── ClientProxy.java
│ ├── gui
│ │ ├── AEBaseGuiContainerDynamic.java
│ │ ├── GuiCraftingTree.java
│ │ ├── util
│ │ │ ├── MousePos.java
│ │ │ ├── RenderFunction.java
│ │ │ ├── RenderPos.java
│ │ │ ├── RenderSize.java
│ │ │ └── TextureProperties.java
│ │ └── widget
│ │ │ ├── Button.java
│ │ │ ├── Button4State.java
│ │ │ ├── Button5State.java
│ │ │ ├── MultiLineLabel.java
│ │ │ ├── Scrollbar.java
│ │ │ ├── SizedColumn.java
│ │ │ ├── SizedRow.java
│ │ │ ├── base
│ │ │ ├── DynamicWidget.java
│ │ │ ├── WidgetController.java
│ │ │ └── WidgetGui.java
│ │ │ ├── container
│ │ │ ├── Column.java
│ │ │ ├── Row.java
│ │ │ ├── ScrollingColumn.java
│ │ │ └── WidgetContainer.java
│ │ │ ├── event
│ │ │ ├── GuiEvent.java
│ │ │ └── WidgetEvent.java
│ │ │ ├── impl
│ │ │ └── craftingtree
│ │ │ │ ├── Background.java
│ │ │ │ ├── CraftingTree.java
│ │ │ │ ├── PlaceHolder.java
│ │ │ │ ├── TreeNode.java
│ │ │ │ ├── TreeRow.java
│ │ │ │ └── event
│ │ │ │ ├── CraftingTreeDataUpdateEvent.java
│ │ │ │ └── TreeNodeSelectEvent.java
│ │ │ └── vanilla
│ │ │ └── GuiButtonImageExt.java
│ └── handler
│ │ └── ClientTickHandler.java
│ ├── common
│ ├── CommonProxy.java
│ ├── container
│ │ └── ContainerCraftingTree.java
│ ├── integration
│ │ ├── JEIUtils.java
│ │ └── ae2
│ │ │ └── data
│ │ │ ├── LiteCraftTreeNode.java
│ │ │ └── LiteCraftTreeProc.java
│ ├── mod
│ │ └── Mods.java
│ ├── network
│ │ ├── PktCraftingTreeData.java
│ │ └── PktSwitchCraftingTree.java
│ └── util
│ │ ├── AEItemStackSet.java
│ │ ├── ByteBufUtils.java
│ │ ├── CraftingTreeProcessUtil.java
│ │ └── NumberUtils.java
│ └── mixin
│ ├── AE2CTLLateMixinLoader.java
│ └── ae2
│ ├── AccessorContainerCraftConfirm.java
│ ├── AccessorCraftingTreeNode.java
│ └── MixinGuiCraftConfirm.java
└── resources
├── assets
└── ae2ctl
│ ├── lang
│ ├── en_US.lang
│ └── zh_CN.lang
│ └── textures
│ └── gui
│ ├── guicraftingtree_256x256_dark.png
│ ├── guicraftingtree_256x256_light.png
│ ├── guicraftingtree_320x256_dark.png
│ ├── guicraftingtree_320x256_light.png
│ ├── guicraftingtree_384x320_dark.png
│ ├── guicraftingtree_384x320_light.png
│ ├── guicraftingtree_512x320_dark.png
│ ├── guicraftingtree_512x320_light.png
│ ├── guicraftingtree_640x384_dark.png
│ ├── guicraftingtree_640x384_light.png
│ ├── guicraftingtree_dark.png
│ └── guicraftingtree_light.png
├── mcmod.info
└── mixins.ae2ctl.json
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build mod
2 |
3 | on: [ push, pull_request, workflow_dispatch ]
4 |
5 | jobs:
6 | build:
7 | name: Build mod
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v3
12 | - name: Set up JDK 1.8
13 | uses: actions/setup-java@v3
14 | with:
15 | distribution: 'adopt'
16 | java-version: '8'
17 | - name: Grant execute permission for gradlew
18 | run: chmod +x gradlew
19 | - name: Build with Gradle
20 | run: ./gradlew -Pnet.minecraftforge.gradle.disableUpdateChecker=true build
21 | - name: Upload artifacts
22 | uses: actions/upload-artifact@v3
23 | with:
24 | name: AE2CT-Legacy
25 | path: build/libs
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # User-specific stuff
2 | .idea/
3 |
4 | *.iml
5 | *.ipr
6 | *.iws
7 |
8 | # IntelliJ
9 | out/
10 | # mpeltonen/sbt-idea plugin
11 | .idea_modules/
12 |
13 | # JIRA plugin
14 | atlassian-ide-plugin.xml
15 |
16 | # Compiled class file
17 | *.class
18 |
19 | # Log file
20 | *.log
21 |
22 | # BlueJ files
23 | *.ctxt
24 |
25 | # Package Files #
26 | *.jar
27 | *.war
28 | *.nar
29 | *.ear
30 | *.zip
31 | *.tar.gz
32 | *.rar
33 |
34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
35 | hs_err_pid*
36 |
37 | *~
38 |
39 | # temporary files which can be created if a process still has a handle open of a deleted file
40 | .fuse_hidden*
41 |
42 | # KDE directory preferences
43 | .directory
44 |
45 | # Linux trash folder which might appear on any partition or disk
46 | .Trash-*
47 |
48 | # .nfs files are created when an open file is removed but is still being accessed
49 | .nfs*
50 |
51 | # General
52 | .DS_Store
53 | .AppleDouble
54 | .LSOverride
55 |
56 | # Icon must end with two \r
57 | Icon
58 |
59 | # Thumbnails
60 | ._*
61 |
62 | # Files that might appear in the root of a volume
63 | .DocumentRevisions-V100
64 | .fseventsd
65 | .Spotlight-V100
66 | .TemporaryItems
67 | .Trashes
68 | .VolumeIcon.icns
69 | .com.apple.timemachine.donotpresent
70 |
71 | # Directories potentially created on remote AFP share
72 | .AppleDB
73 | .AppleDesktop
74 | Network Trash Folder
75 | Temporary Items
76 | .apdisk
77 |
78 | # Windows thumbnail cache files
79 | Thumbs.db
80 | Thumbs.db:encryptable
81 | ehthumbs.db
82 | ehthumbs_vista.db
83 |
84 | # Dump file
85 | *.stackdump
86 |
87 | # Folder config file
88 | [Dd]esktop.ini
89 |
90 | # Recycle Bin used on file shares
91 | $RECYCLE.BIN/
92 |
93 | # Windows Installer files
94 | *.cab
95 | *.msi
96 | *.msix
97 | *.msm
98 | *.msp
99 |
100 | # Windows shortcuts
101 | *.lnk
102 |
103 | .gradle
104 | build/
105 |
106 | # Ignore Gradle GUI config
107 | gradle-app.setting
108 |
109 | # Cache of project
110 | .gradletasknamecache
111 |
112 | **/build/
113 |
114 | # Common working directory
115 | run/
116 |
117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
118 | !gradle-wrapper.jar
119 |
120 | gradle.properties
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.gradle.ext.Gradle
2 | import org.jetbrains.gradle.ext.RunConfigurationContainer
3 | import java.util.*
4 |
5 | plugins {
6 | id("java-library")
7 | id("maven-publish")
8 | id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7"
9 | id("eclipse")
10 | id("com.gtnewhorizons.retrofuturagradle") version "1.3.19"
11 | }
12 |
13 | // Project properties
14 | group = "github.kasuminova.ae2ctl"
15 | version = "0.1.2"
16 |
17 | // Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod
18 | java {
19 | toolchain {
20 | languageVersion.set(JavaLanguageVersion.of(17))
21 | // Azul covers the most platforms for Java 8 toolchains, crucially including MacOS arm64
22 | vendor.set(JvmVendorSpec.AZUL)
23 | }
24 | // Generate sources and javadocs jars when building and publishing
25 | withSourcesJar()
26 | withJavadocJar()
27 | }
28 |
29 | // Most RFG configuration lives here, see the JavaDoc for com.gtnewhorizons.retrofuturagradle.MinecraftExtension
30 | minecraft {
31 | mcVersion.set("1.12.2")
32 |
33 | // Username for client run configurations
34 | username.set("Kasumi_Nova")
35 |
36 | // Generate a field named VERSION with the mod version in the injected Tags class
37 | injectedTags.put("VERSION", project.version)
38 |
39 | // If you need the old replaceIn mechanism, prefer the injectTags task because it doesn't inject a javac plugin.
40 | // tagReplacementFiles.add("RfgExampleMod.java")
41 |
42 | // Enable assertions in the mod's package when running the client or server
43 | val args = mutableListOf("-ea:${project.group}")
44 |
45 | // Mixin args
46 | args.add("-Dmixin.hotSwap=true")
47 | args.add("-Dmixin.checks.interfaces=true")
48 | args.add("-Dmixin.debug.export=true")
49 | extraRunJvmArguments.addAll(args)
50 |
51 | // If needed, add extra tweaker classes like for mixins.
52 | // extraTweakClasses.add("org.spongepowered.asm.launch.MixinTweaker")
53 |
54 | // Exclude some Maven dependency groups from being automatically included in the reobfuscated runs
55 | groupsToExcludeFromAutoReobfMapping.addAll("com.diffplug", "com.diffplug.durian", "net.industrial-craft")
56 | }
57 |
58 | // Generates a class named rfg.examplemod.Tags with the mod version in it, you can find it at
59 | tasks.injectTags.configure {
60 | outputClassName.set("${project.group}.Tags")
61 | }
62 |
63 | // Put the version from gradle into mcmod.info
64 | tasks.processResources.configure {
65 | // inputs.property("version", project.version)
66 | //
67 | // filesMatching("mcmod.info") {
68 | // expand(mapOf("version" to project.version))
69 | // }
70 | }
71 |
72 | tasks.compileJava.configure {
73 | sourceCompatibility = "17"
74 | options.release = 8
75 | options.encoding = "UTF-8" // Use the UTF-8 charset for Java compilation
76 |
77 | javaCompiler = javaToolchains.compilerFor {
78 | languageVersion = JavaLanguageVersion.of(17)
79 | }
80 | }
81 |
82 | tasks.compileTestJava.configure {
83 | sourceCompatibility = "17"
84 | options.release = 8
85 | options.encoding = "UTF-8" // Use the UTF-8 charset for Java compilation
86 |
87 | javaCompiler = javaToolchains.compilerFor {
88 | languageVersion = JavaLanguageVersion.of(17)
89 | }
90 | }
91 |
92 | tasks.javadoc.configure {
93 | // No need for JavaDoc.
94 | actions = Collections.emptyList()
95 | }
96 |
97 | //tasks.deobfuscateMergedJarToSrg.configure {
98 | // accessTransformerFiles.from("src/main/resources/META-INF/lumenized_at.cfg")
99 | //}
100 | //tasks.srgifyBinpatchedJar.configure {
101 | // accessTransformerFiles.from("src/main/resources/META-INF/lumenized_at.cfg")
102 | //}
103 |
104 | // Create a new dependency type for runtime-only dependencies that don't get included in the maven publication
105 | val runtimeOnlyNonPublishable: Configuration by configurations.creating {
106 | description = "Runtime only dependencies that are not published alongside the jar"
107 | isCanBeConsumed = false
108 | isCanBeResolved = false
109 | }
110 | listOf(configurations.runtimeClasspath, configurations.testRuntimeClasspath).forEach {
111 | it.configure {
112 | extendsFrom(
113 | runtimeOnlyNonPublishable
114 | )
115 | }
116 | }
117 |
118 | // Dependencies
119 | repositories {
120 | flatDir {
121 | dirs("lib")
122 | }
123 | maven {
124 | url = uri("https://maven.aliyun.com/nexus/content/groups/public/")
125 | }
126 | maven {
127 | url = uri("https://maven.aliyun.com/nexus/content/repositories/jcenter")
128 | }
129 | maven {
130 | url = uri("https://maven.cleanroommc.com")
131 | }
132 | maven {
133 | url = uri("https://cfa2.cursemaven.com")
134 | }
135 | maven {
136 | url = uri("https://cursemaven.com")
137 | }
138 | maven {
139 | url = uri("https://maven.blamejared.com/")
140 | }
141 | maven {
142 | url = uri("https://repo.spongepowered.org/maven")
143 | }
144 | maven {
145 | name = "GeckoLib"
146 | url = uri("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/")
147 | }
148 | maven {
149 | name = "OvermindDL1 Maven"
150 | url = uri("https://gregtech.overminddl1.com/")
151 | mavenContent {
152 | excludeGroup("net.minecraftforge") // missing the `universal` artefact
153 | }
154 | }
155 | maven {
156 | name = "GTNH Maven"
157 | url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/")
158 | isAllowInsecureProtocol = true
159 | }
160 | }
161 |
162 | dependencies {
163 | annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2")
164 | compileOnly("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2")
165 | // workaround for https://github.com/bsideup/jabel/issues/174
166 | annotationProcessor("net.java.dev.jna:jna-platform:5.13.0")
167 | // Allow jdk.unsupported classes like sun.misc.Unsafe, workaround for JDK-8206937 and fixes Forge crashes in tests.
168 | patchedMinecraft("me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0")
169 | // allow Jabel to work in tests
170 | testAnnotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:1.0.0")
171 | testCompileOnly("com.github.bsideup.jabel:jabel-javac-plugin:1.0.0") {
172 | isTransitive = false // We only care about the 1 annotation class
173 | }
174 | testCompileOnly("me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0")
175 |
176 | // Mixins
177 | // implementation("zone.rong:mixinbooter:7.1")
178 | val mixin : String = modUtils.enableMixins("zone.rong:mixinbooter:8.9", "mixins.ae2ctl.refmap.json").toString()
179 | api (mixin) {
180 | isTransitive = false
181 | }
182 | annotationProcessor("org.ow2.asm:asm-debug-all:5.2")
183 | annotationProcessor("com.google.guava:guava:30.0-jre")
184 | annotationProcessor("com.google.code.gson:gson:2.8.9")
185 | annotationProcessor (mixin) {
186 | isTransitive = false
187 | }
188 | compileOnlyApi("org.jetbrains:annotations:24.1.0")
189 | annotationProcessor("org.jetbrains:annotations:24.1.0")
190 |
191 | // Mod Dependencies
192 | implementation(rfg.deobf("curse.maven:CodeChickenLib-242818:2779848"))
193 | implementation(rfg.deobf("curse.maven:ae2-extended-life-570458:5378163"))
194 | implementation(rfg.deobf("curse.maven:had-enough-items-557549:4810661"))
195 | }
196 |
197 | idea {
198 | module {
199 | isDownloadJavadoc = true
200 | isDownloadSources = true
201 | inheritOutputDirs = true // Fix resources in IJ-Native runs
202 | }
203 | project {
204 | this.withGroovyBuilder {
205 | "settings" {
206 | "runConfigurations" {
207 | val self = this.delegate as RunConfigurationContainer
208 | self.add(Gradle("1. Run Client").apply {
209 | setProperty("taskNames", listOf("runClient"))
210 | })
211 | self.add(Gradle("2. Run Server").apply {
212 | setProperty("taskNames", listOf("runServer"))
213 | })
214 | self.add(Gradle("3. Run Obfuscated Client").apply {
215 | setProperty("taskNames", listOf("runObfClient"))
216 | })
217 | self.add(Gradle("4. Run Obfuscated Server").apply {
218 | setProperty("taskNames", listOf("runObfServer"))
219 | })
220 | }
221 | "compiler" {
222 | val self = this.delegate as org.jetbrains.gradle.ext.IdeaCompilerConfiguration
223 | afterEvaluate {
224 | self.javac.moduleJavacAdditionalOptions = mapOf(
225 | (project.name + ".main") to
226 | tasks.compileJava.get().options.compilerArgs.map { '"' + it + '"' }.joinToString(" ")
227 | )
228 | }
229 | }
230 | }
231 | }
232 | }
233 | }
234 |
235 | tasks.processIdeaSettings.configure {
236 | dependsOn(tasks.injectTags)
237 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/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.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 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | maven {
4 | // RetroFuturaGradle
5 | name = "GTNH Maven"
6 | url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/")
7 | isAllowInsecureProtocol = true
8 | mavenContent {
9 | includeGroup("com.gtnewhorizons")
10 | includeGroup("com.gtnewhorizons.retrofuturagradle")
11 | }
12 | }
13 | gradlePluginPortal()
14 | mavenCentral()
15 | mavenLocal()
16 | }
17 | }
18 |
19 | plugins {
20 | // Automatic toolchain provisioning
21 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0"
22 | }
23 |
24 | rootProject.name = "AE2CT-Legacy"
25 |
--------------------------------------------------------------------------------
/src/api/java/yalter/mousetweaks/api/IMTModGuiContainer2.java:
--------------------------------------------------------------------------------
1 | package yalter.mousetweaks.api;
2 |
3 | import net.minecraft.inventory.Container;
4 | import net.minecraft.inventory.Slot;
5 |
6 | /**
7 | * This is the interface you want to implement in your GuiScreen to make it compatible with Mouse Tweaks.
8 | * If this interface is not enough (for example, you need a custom slot click function, or if you use a custom Container
9 | * which happens to be incompatible), check IMTModGuiContainer2Ex instead.
10 | * If you just need to disable Mouse Tweaks or the wheel tweak, see the MouseTweaksIgnore
11 | * or the MouseTweaksDisableWheelTweak annotations.
12 | */
13 | public interface IMTModGuiContainer2 {
14 | /**
15 | * If you want to disable Mouse Tweaks in your GuiScreen, return true from this method.
16 | *
17 | * @return True if Mouse Tweaks should be disabled, false otherwise.
18 | */
19 | boolean MT_isMouseTweaksDisabled();
20 |
21 | /**
22 | * If you want to disable the Wheel Tweak in your GuiScreen, return true from this method.
23 | *
24 | * @return True if the Wheel Tweak should be disabled, false otherwise.
25 | */
26 | boolean MT_isWheelTweakDisabled();
27 |
28 | /**
29 | * Returns the Container.
30 | *
31 | * @return Container that is currently in use.
32 | */
33 | Container MT_getContainer();
34 |
35 | /**
36 | * Returns the Slot that is currently selected by the player, or null if no Slot is selected.
37 | *
38 | * @return Slot that is located under the mouse, or null if no Slot it currently under the mouse.
39 | */
40 | Slot MT_getSlotUnderMouse();
41 |
42 | /**
43 | * Return true if the given Slot behaves like the vanilla crafting output slots (inside the crafting table,
44 | * or the furnace output slot, or the anvil output slot, etc.). These slots are handled differently by Mouse Tweaks.
45 | *
46 | * @param slot the slot to check
47 | * @return True if slot is a crafting output slot.
48 | */
49 | boolean MT_isCraftingOutput(Slot slot);
50 |
51 | /**
52 | * Return true if the given Slot should be ignored by Mouse Tweaks. Examples of ignored slots are the item select
53 | * slots and the Destroy Item slot in the vanilla creative inventory.
54 | *
55 | * @param slot the slot to check
56 | * @return Tru if slot should be ignored by Mouse Tweaks.
57 | */
58 | boolean MT_isIgnored(Slot slot);
59 |
60 | /**
61 | * If your container has an RMB dragging functionality (like vanilla containers), disable it inside this method.
62 | * This method is called every frame (render tick), which is after all mouseClicked / mouseClickMove / mouseReleased
63 | * events are handled (although note these events are handled every game tick, which is far less frequent than every
64 | * render tick).
65 | *
66 | * If true is returned from this method, Mouse Tweaks (after checking other conditions like isIgnored) will click
67 | * the slot on which the right mouse button was initially pressed (in most cases this is the slot currently under
68 | * mouse). This is needed because the vanilla RMB dragging functionality prevents the initial slot click.
69 | *
70 | * For vanilla containers this method looks like this:
71 | *
72 | * this.ignoreMouseUp = true;
73 | *
74 | * if (this.dragSplitting) {
75 | * if (this.dragSplittingButton == 1) {
76 | * this.dragSplitting = false;
77 | * return true;
78 | * }
79 | * }
80 | *
81 | * return false;
82 | *
83 | *
84 | * @return True if Mouse Tweaks should click the slot on which the RMB was pressed.
85 | */
86 | boolean MT_disableRMBDraggingFunctionality();
87 | }
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/AE2CTLegacy.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl;
2 |
3 | import github.kasuminova.ae2ctl.common.CommonProxy;
4 | import github.kasuminova.ae2ctl.common.network.PktCraftingTreeData;
5 | import github.kasuminova.ae2ctl.common.network.PktSwitchCraftingTree;
6 | import net.minecraftforge.fml.common.Mod;
7 | import net.minecraftforge.fml.common.SidedProxy;
8 | import net.minecraftforge.fml.common.event.*;
9 | import net.minecraftforge.fml.common.network.NetworkRegistry;
10 | import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
11 | import net.minecraftforge.fml.relauncher.Side;
12 | import org.apache.logging.log4j.Logger;
13 |
14 | @Mod(modid = AE2CTLegacy.MOD_ID, name = AE2CTLegacy.MOD_NAME, version = AE2CTLegacy.VERSION,
15 | dependencies = "required-after:forge@[14.23.5.0,);" +
16 | "required-after:appliedenergistics2;" +
17 | "required-after:jei;" +
18 | "required-after:mixinbooter@[8.0,);",
19 | acceptedMinecraftVersions = "[1.12, 1.13)",
20 | acceptableRemoteVersions = "[0.1.0, 0.2.0)"
21 | )
22 | @SuppressWarnings("MethodMayBeStatic")
23 | public class AE2CTLegacy {
24 | public static final String MOD_ID = "ae2ctl";
25 | public static final String MOD_NAME = "AE2 Crafting Tree - Legacy";
26 |
27 | public static final String VERSION = Tags.VERSION;
28 |
29 | public static final String CLIENT_PROXY = "github.kasuminova.ae2ctl.client.ClientProxy";
30 | public static final String COMMON_PROXY = "github.kasuminova.ae2ctl.common.CommonProxy";
31 |
32 | public static final SimpleNetworkWrapper NET_CHANNEL = NetworkRegistry.INSTANCE.newSimpleChannel(MOD_ID);
33 |
34 | @Mod.Instance(MOD_ID)
35 | public static AE2CTLegacy instance = null;
36 |
37 | @SidedProxy(clientSide = CLIENT_PROXY, serverSide = COMMON_PROXY)
38 | public static CommonProxy proxy = null;
39 |
40 | public static Logger log = null;
41 |
42 | public AE2CTLegacy() {
43 | }
44 |
45 | @Mod.EventHandler
46 | public void construction(FMLConstructionEvent event) {
47 | proxy.construction();
48 | }
49 |
50 | @Mod.EventHandler
51 | public void preInit(FMLPreInitializationEvent event) {
52 | event.getModMetadata().version = VERSION;
53 | log = event.getModLog();
54 |
55 | NET_CHANNEL.registerMessage(PktCraftingTreeData.class, PktCraftingTreeData.class, 0, Side.CLIENT);
56 | NET_CHANNEL.registerMessage(PktSwitchCraftingTree.class, PktSwitchCraftingTree.class, 1, Side.SERVER);
57 |
58 | proxy.preInit();
59 | }
60 |
61 | @Mod.EventHandler
62 | public void init(FMLInitializationEvent event) {
63 | proxy.init();
64 | }
65 |
66 | @Mod.EventHandler
67 | public void postInit(FMLPostInitializationEvent event) {
68 | proxy.postInit();
69 | }
70 |
71 | @Mod.EventHandler
72 | public void loadComplete(FMLLoadCompleteEvent event) {
73 | proxy.loadComplete();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/ClientProxy.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client;
2 |
3 |
4 | import appeng.api.storage.ITerminalHost;
5 | import appeng.container.implementations.ContainerCraftConfirm;
6 | import github.kasuminova.ae2ctl.client.gui.GuiCraftingTree;
7 | import github.kasuminova.ae2ctl.client.handler.ClientTickHandler;
8 | import github.kasuminova.ae2ctl.common.CommonProxy;
9 | import net.minecraft.entity.player.EntityPlayer;
10 | import net.minecraft.util.math.MathHelper;
11 | import net.minecraft.world.World;
12 | import net.minecraftforge.common.MinecraftForge;
13 |
14 | import javax.annotation.Nullable;
15 |
16 | public class ClientProxy extends CommonProxy {
17 |
18 | @Override
19 | public void construction() {
20 | super.construction();
21 | }
22 |
23 | @Override
24 | public void preInit() {
25 | super.preInit();
26 | MinecraftForge.EVENT_BUS.register(ClientTickHandler.class);
27 | }
28 |
29 | @Override
30 | public void init() {
31 | super.init();
32 | }
33 |
34 | @Override
35 | public void postInit() {
36 | super.postInit();
37 | }
38 |
39 | @Override
40 | public void loadComplete() {
41 | super.loadComplete();
42 | }
43 |
44 | @Nullable
45 | @Override
46 | public Object getClientGuiElement(final int ID, final EntityPlayer player, final World world, final int x, final int y, final int z) {
47 | GuiType type = GuiType.values()[MathHelper.clamp(ID, 0, GuiType.values().length - 1)];
48 | return switch (type) {
49 | case CRAFTING_TREE -> {
50 | if (!(player.openContainer instanceof ContainerCraftConfirm confirm)) {
51 | yield null;
52 | }
53 | yield new GuiCraftingTree(player.inventory, (ITerminalHost) confirm.getTarget());
54 | }
55 | };
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/AEBaseGuiContainerDynamic.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui;
2 |
3 | import appeng.client.gui.AEBaseGui;
4 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
5 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetController;
6 | import net.minecraft.client.gui.FontRenderer;
7 | import net.minecraft.inventory.Container;
8 | import net.minecraft.inventory.Slot;
9 | import net.minecraft.item.ItemStack;
10 | import net.minecraftforge.fml.client.config.GuiUtils;
11 | import org.lwjgl.input.Mouse;
12 |
13 | import javax.annotation.Nonnull;
14 | import java.io.IOException;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | public abstract class AEBaseGuiContainerDynamic extends AEBaseGui {
19 |
20 | protected WidgetController widgetController = null;
21 | protected Slot hoveredSlot = null;
22 |
23 | public AEBaseGuiContainerDynamic(final Container container) {
24 | super(container);
25 | }
26 |
27 | @Override
28 | public void updateScreen() {
29 | super.updateScreen();
30 | widgetController.update();
31 | }
32 |
33 | @Override
34 | public void initGui() {
35 | super.initGui();
36 | widgetController.getGui()
37 | .setGuiLeft(guiLeft)
38 | .setGuiTop(guiTop)
39 | .setXSize(width)
40 | .setYSize(height);
41 | widgetController.init();
42 | }
43 |
44 | @Override
45 | public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
46 | widgetController.postRender(new MousePos(mouseX, mouseY), false);
47 | }
48 |
49 | @Override
50 | public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
51 | widgetController.render(new MousePos(mouseX, mouseY), true);
52 | }
53 |
54 | @Override
55 | protected void renderHoveredToolTip(final int mouseX, final int mouseY) {
56 | updateHoveredSlot(mouseX, mouseY);
57 |
58 | ItemStack stackInSlot = hoveredSlot == null ? ItemStack.EMPTY : hoveredSlot.getStack();
59 | List hoverTooltips = widgetController.getHoverTooltips(new MousePos(mouseX, mouseY));
60 | if (stackInSlot.isEmpty() && hoverTooltips.isEmpty()) {
61 | return;
62 | }
63 | List itemTooltip = stackInSlot.isEmpty() ? new ArrayList<>() : this.getItemToolTip(stackInSlot);
64 | itemTooltip.addAll(hoverTooltips);
65 |
66 | FontRenderer font = stackInSlot.getItem().getFontRenderer(stackInSlot);
67 | GuiUtils.preItemToolTip(stackInSlot);
68 | this.drawHoveringText(itemTooltip, mouseX, mouseY, (font == null ? fontRenderer : font));
69 | GuiUtils.postItemToolTip();
70 | }
71 |
72 | @Override
73 | public void handleMouseInput() throws IOException {
74 | final int mouseX = Mouse.getEventX() * this.width / this.mc.displayWidth;
75 | final int mouseY = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
76 | if (widgetController.onMouseInput(new MousePos(mouseX, mouseY))) {
77 | return;
78 | }
79 | super.handleMouseInput();
80 | }
81 |
82 | @Override
83 | protected void mouseClicked(final int mouseX, final int mouseY, final int mouseButton) throws IOException {
84 | if (widgetController.onMouseClicked(new MousePos(mouseX, mouseY), mouseButton)) {
85 | return;
86 | }
87 | super.mouseClicked(mouseX, mouseY, mouseButton);
88 | }
89 |
90 | @Override
91 | protected void mouseReleased(final int mouseX, final int mouseY, final int state) {
92 | if (widgetController.onMouseReleased(new MousePos(mouseX, mouseY))) {
93 | return;
94 | }
95 | super.mouseReleased(mouseX, mouseY, state);
96 | }
97 |
98 | @Override
99 | protected void keyTyped(final char typedChar, final int keyCode) throws IOException {
100 | if (widgetController.onKeyTyped(typedChar, keyCode)) {
101 | return;
102 | }
103 | super.keyTyped(typedChar, keyCode);
104 | }
105 |
106 | @Override
107 | public void drawHoveringText(@Nonnull final List textLines, final int x, final int y) {
108 | super.drawHoveringText(textLines, x, y);
109 | }
110 |
111 | protected void updateHoveredSlot(final int mouseX, final int mouseY) {
112 | hoveredSlot = this.inventorySlots.inventorySlots.stream()
113 | .filter(slot -> this.isMouseOverSlot(slot, mouseX, mouseY) && slot.isEnabled())
114 | .findFirst()
115 | .orElse(null);
116 | }
117 |
118 | protected boolean isMouseOverSlot(Slot slotIn, int mouseX, int mouseY) {
119 | return this.isPointInRegion(slotIn.xPos, slotIn.yPos, 16, 16, mouseX, mouseY);
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/GuiCraftingTree.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui;
2 |
3 | import appeng.api.storage.ITerminalHost;
4 | import github.kasuminova.ae2ctl.AE2CTLegacy;
5 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
6 | import github.kasuminova.ae2ctl.client.gui.widget.Button4State;
7 | import github.kasuminova.ae2ctl.client.gui.widget.Button5State;
8 | import github.kasuminova.ae2ctl.client.gui.widget.MultiLineLabel;
9 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetController;
10 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
11 | import github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.Background;
12 | import github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.CraftingTree;
13 | import github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.event.CraftingTreeDataUpdateEvent;
14 | import github.kasuminova.ae2ctl.common.container.ContainerCraftingTree;
15 | import github.kasuminova.ae2ctl.common.integration.ae2.data.LiteCraftTreeNode;
16 | import github.kasuminova.ae2ctl.common.network.PktSwitchCraftingTree;
17 | import net.minecraft.client.gui.Gui;
18 | import net.minecraft.client.renderer.GlStateManager;
19 | import net.minecraft.client.resources.I18n;
20 | import net.minecraft.entity.player.InventoryPlayer;
21 | import net.minecraft.util.ResourceLocation;
22 |
23 | import java.util.Collections;
24 |
25 | /**
26 | * TODO: Dark mode switch.
27 | */
28 | public class GuiCraftingTree extends AEBaseGuiContainerDynamic {
29 |
30 | private static final TextureProperties BUTTON_BACK = TextureProperties.of(
31 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
32 | 232, 100, 24, 20
33 | );
34 | private static final TextureProperties BUTTON_BACK_HOVERED = TextureProperties.of(
35 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
36 | 208, 100, 24, 20
37 | );
38 | private static final TextureProperties BUTTON_BACK_MOUSEDOWN = TextureProperties.of(
39 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
40 | 184, 100, 24, 20
41 | );
42 |
43 | private static final TextureProperties BUTTON_MISSING_ONLY = TextureProperties.of(
44 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
45 | 232, 60, 24, 20
46 | );
47 | private static final TextureProperties BUTTON_MISSING_ONLY_HOVERED = TextureProperties.of(
48 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
49 | 208, 60, 24, 20
50 | );
51 | private static final TextureProperties BUTTON_MISSING_ONLY_MOUSEDOWN = TextureProperties.of(
52 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
53 | 184, 60, 24, 20
54 | );
55 | private static final TextureProperties BUTTON_MISSING_ONLY_CLICKED = TextureProperties.of(
56 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
57 | 160, 60, 24, 20
58 | );
59 |
60 | private static final TextureProperties BUTTON_SCREENSHOT = TextureProperties.of(
61 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
62 | 232, 0, 24, 20
63 | );
64 | private static final TextureProperties BUTTON_SCREENSHOT_HOVERED = TextureProperties.of(
65 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
66 | 208, 0, 24, 20
67 | );
68 | private static final TextureProperties BUTTON_SCREENSHOT_MOUSEDOWN = TextureProperties.of(
69 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
70 | 184, 0, 24, 20
71 | );
72 |
73 | private final CraftingTree tree = new CraftingTree();
74 |
75 | private final Button4State screenshot = new Button4State();
76 | private final Button5State missingOnly = new Button5State();
77 | private final Button4State back = new Button4State();
78 |
79 | private Background background;
80 |
81 | public GuiCraftingTree(final InventoryPlayer inventoryPlayer, final ITerminalHost te) {
82 | super(new ContainerCraftingTree(inventoryPlayer, te));
83 | this.background = Background.getLargest(this.width, this.height, true);
84 | this.xSize = background.getTexture().width();
85 | this.ySize = background.getTexture().height();
86 | this.guiLeft = (this.width - this.xSize) / 2;
87 | this.guiTop = (this.height - this.ySize) / 2;
88 |
89 | this.widgetController = new WidgetController(WidgetGui.of(this, this.xSize, this.ySize, guiLeft, guiTop));
90 | this.widgetController.addWidget(tree
91 | .setWidthHeight(background.getInternalWidth(), background.getInternalHeight())
92 | .setAbsXY(background.getInternalXOffset(), background.getInternalYOffset())
93 | );
94 | // Title
95 | this.widgetController.addWidget(new MultiLineLabel(Collections.singletonList(I18n.format("gui.crafting_tree.title")))
96 | .setAutoWrap(false)
97 | .setMargin(0)
98 | .setAbsXY(6, 9)
99 | );
100 | // Right Top
101 | this.widgetController.addWidget(screenshot
102 | .setMouseDownTexture(BUTTON_SCREENSHOT_MOUSEDOWN)
103 | .setHoveredTexture(BUTTON_SCREENSHOT_HOVERED)
104 | .setTexture(BUTTON_SCREENSHOT)
105 | .setTooltipFunction(input -> Collections.singletonList(I18n.format("gui.crafting_tree.screenshot")))
106 | .setOnClickedListener(btn -> {})
107 | .setWidthHeight(24, 20)
108 | .setAbsXY(xSize - 86, 3));
109 | this.widgetController.addWidget(missingOnly
110 | .setClickedTexture(BUTTON_MISSING_ONLY_CLICKED)
111 | .setMouseDownTexture(BUTTON_MISSING_ONLY_MOUSEDOWN)
112 | .setHoveredTexture(BUTTON_MISSING_ONLY_HOVERED)
113 | .setTexture(BUTTON_MISSING_ONLY)
114 | .setTooltipFunction(input -> missingOnly.isClicked()
115 | ? Collections.singletonList(I18n.format("gui.crafting_tree.default"))
116 | : Collections.singletonList(I18n.format("gui.crafting_tree.missing_only"))
117 | )
118 | .setOnClickedListener(btn -> tree.setMissingOnly(missingOnly.isClicked()))
119 | .setWidthHeight(24, 20)
120 | .setAbsXY(xSize - 58, 3));
121 | this.widgetController.addWidget(back
122 | .setMouseDownTexture(BUTTON_BACK_MOUSEDOWN)
123 | .setHoveredTexture(BUTTON_BACK_HOVERED)
124 | .setTexture(BUTTON_BACK)
125 | .setTooltipFunction(input -> Collections.singletonList(I18n.format("gui.crafting_tree.back")))
126 | .setOnClickedListener(btn -> AE2CTLegacy.NET_CHANNEL.sendToServer(new PktSwitchCraftingTree()))
127 | .setWidthHeight(24, 20)
128 | .setAbsXY(xSize - 30, 3));
129 | }
130 |
131 | @Override
132 | public void initGui() {
133 | this.background = Background.getLargest(this.width, this.height, true);
134 | this.xSize = background.getTexture().width();
135 | this.ySize = background.getTexture().height();
136 | this.guiLeft = (this.width - this.xSize) / 2;
137 | this.guiTop = (this.height - this.ySize) / 2;
138 |
139 | this.tree.setWidthHeight(background.getInternalWidth(), background.getInternalHeight())
140 | .setAbsXY(background.getInternalXOffset(), background.getInternalYOffset());
141 | this.screenshot.setAbsXY(xSize - 86, 3);
142 | this.missingOnly.setAbsXY(xSize - 58, 3);
143 | this.back.setAbsXY(xSize - 30, 3);
144 |
145 | super.initGui();
146 | }
147 |
148 | @Override
149 | public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
150 | GlStateManager.color(1.0F, 1.0F, 1.0F);
151 |
152 | assert background.getTexture().texRes() != null;
153 | this.mc.getTextureManager().bindTexture(background.getTexture().texRes());
154 | final int x = (this.width - this.xSize) / 2;
155 | final int y = (this.height - this.ySize) / 2;
156 | Gui.drawModalRectWithCustomSizedTexture(x, y, 0, 0, xSize, ySize, xSize, ySize);
157 |
158 | super.drawBG(offsetX, offsetY, mouseX, mouseY);
159 | }
160 |
161 | public void onDataUpdate(final LiteCraftTreeNode root) {
162 | if (LiteCraftTreeNode.isMissing(root)) {
163 | this.missingOnly.setClicked(true);
164 | }
165 | this.widgetController.postGuiEvent(new CraftingTreeDataUpdateEvent(root));
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/util/MousePos.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.util;
2 |
3 | import com.github.bsideup.jabel.Desugar;
4 |
5 | @Desugar
6 | public record MousePos(int mouseX, int mouseY) {
7 |
8 | public MousePos relativeTo(RenderPos renderPos) {
9 | return new MousePos(mouseX - renderPos.posX(), mouseY - renderPos.posY());
10 | }
11 |
12 | public MousePos add(RenderPos renderPos) {
13 | return new MousePos(mouseX + renderPos.posX(), mouseY + renderPos.posY());
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/util/RenderFunction.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.util;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
4 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
5 |
6 | @FunctionalInterface
7 | public interface RenderFunction {
8 |
9 | void doRender(DynamicWidget dynamicWidget, WidgetGui gui, RenderSize renderSize, RenderPos renderPos, MousePos mousePos);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/util/RenderPos.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.util;
2 |
3 | import com.github.bsideup.jabel.Desugar;
4 |
5 | @Desugar
6 | public record RenderPos(int posX, int posY) {
7 |
8 | public RenderPos add(RenderPos another) {
9 | return new RenderPos(posX + another.posX, posY + another.posY);
10 | }
11 |
12 | public RenderPos subtract(RenderPos another) {
13 | return new RenderPos(posX - another.posX, posY - another.posY);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/util/RenderSize.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.util;
2 |
3 | import com.github.bsideup.jabel.Desugar;
4 |
5 | @Desugar
6 | public record RenderSize(int width, int height) {
7 |
8 | public boolean isLimited() {
9 | return width != -1 && height != -1;
10 | }
11 |
12 | public boolean isWidthLimited() {
13 | return width != -1;
14 | }
15 |
16 | public boolean isHeightLimited() {
17 | return height != -1;
18 | }
19 |
20 | public RenderSize smaller(final RenderSize another) {
21 | return new RenderSize(Math.min(width, another.width), Math.min(height, another.height));
22 | }
23 |
24 | public RenderSize add(final RenderSize another) {
25 | return new RenderSize(width + another.width, height + another.height);
26 | }
27 |
28 | public RenderSize subtract(final RenderSize another) {
29 | return new RenderSize(width - another.width, height - another.height);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/util/TextureProperties.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.util;
2 |
3 | import com.github.bsideup.jabel.Desugar;
4 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
5 | import net.minecraft.client.gui.GuiScreen;
6 | import net.minecraft.client.renderer.texture.TextureManager;
7 | import net.minecraft.util.ResourceLocation;
8 |
9 | import javax.annotation.Nullable;
10 | import javax.annotation.ParametersAreNonnullByDefault;
11 | import java.util.Optional;
12 | import java.util.function.Consumer;
13 |
14 | @Desugar
15 | @ParametersAreNonnullByDefault
16 | @SuppressWarnings("unused")
17 | public record TextureProperties(@Nullable ResourceLocation texRes, int texX, int texY, int width, int height) {
18 |
19 | public static final TextureProperties EMPTY = new TextureProperties(null, 0, 0, 0, 0);
20 |
21 | public static TextureProperties of(@Nullable final ResourceLocation texRes, final int texX, final int texY, final int width, final int height) {
22 | return new TextureProperties(texRes, texX, texY, width, height);
23 | }
24 |
25 | public static TextureProperties of(final int texX, final int texY, final int width, final int height) {
26 | return of(null, texX, texY, width, height);
27 | }
28 |
29 | public static TextureProperties of(final int texX, final int texY, final int widthHeight) {
30 | return of(null, texX, texY, widthHeight, widthHeight);
31 | }
32 |
33 | public static TextureProperties of(@Nullable final ResourceLocation texRes, final int texX, final int texY) {
34 | return of(texRes, texX, texY, 0, 0);
35 | }
36 |
37 | public static TextureProperties of(final int texX, final int texY) {
38 | return of(null, texX, texY);
39 | }
40 |
41 | public void bind(final TextureManager textureManager) {
42 | if (texRes != null) {
43 | bind(textureManager, texRes);
44 | }
45 | }
46 |
47 | private static void bind(final TextureManager textureManager, final ResourceLocation texRes) {
48 | textureManager.bindTexture(texRes);
49 | }
50 |
51 | // Default render
52 |
53 | public void render(final RenderPos renderPos, final WidgetGui gui) {
54 | render(renderPos, gui.getGui());
55 | }
56 |
57 | public void render(final RenderPos renderPos, final GuiScreen gui) {
58 | bind(gui.mc.getTextureManager());
59 | gui.drawTexturedModalRect(renderPos.posX(), renderPos.posY(), texX, texY, width, height);
60 | }
61 |
62 | public void renderIfPresent(final RenderPos renderPos, final WidgetGui gui) {
63 | ifPresent(t -> t.render(renderPos, gui));
64 | }
65 |
66 | public void renderIfPresent(final RenderPos renderPos, final GuiScreen gui) {
67 | ifPresent(t -> t.render(renderPos, gui));
68 | }
69 |
70 | public void renderIfPresent(final RenderPos renderPos, final WidgetGui gui, final Consumer before) {
71 | ifPresent(t -> before.andThen(t1 -> t.render(renderPos, gui)).accept(t));
72 | }
73 |
74 | public void renderIfPresent(final RenderPos renderPos, final GuiScreen gui, final Consumer before) {
75 | ifPresent(t -> before.andThen(t1 -> t.render(renderPos, gui)).accept(t));
76 | }
77 |
78 | public void renderIfPresent(final RenderPos renderPos, final WidgetGui gui, @Nullable final Consumer before, @Nullable final Consumer after) {
79 | ifPresent(t -> {
80 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
81 | t.render(renderPos, gui);
82 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
83 | });
84 | }
85 |
86 | public void renderIfPresent(final RenderPos renderPos, final GuiScreen gui, @Nullable final Consumer before, @Nullable final Consumer after) {
87 | ifPresent(t -> {
88 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
89 | t.render(renderPos, gui);
90 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
91 | });
92 | }
93 |
94 | public void ifPresent(final Consumer runnable) {
95 | if (texRes != null && width > 0 && height > 0) {
96 | runnable.accept(this);
97 | }
98 | }
99 |
100 | // Custom texture render
101 |
102 | public void render(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final WidgetGui gui) {
103 | render(customTexRes, renderPos, gui.getGui());
104 | }
105 |
106 | public void render(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final GuiScreen gui) {
107 | TextureManager textureManager = gui.mc.getTextureManager();
108 | if (texRes != null) {
109 | bind(textureManager);
110 | } else {
111 | if (customTexRes != null) {
112 | bind(textureManager, customTexRes);
113 | } else {
114 | return;
115 | }
116 | }
117 | gui.drawTexturedModalRect(renderPos.posX(), renderPos.posY(), texX, texY, width, height);
118 | }
119 |
120 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final WidgetGui gui) {
121 | ifSizePresent(t -> t.render(customTexRes, renderPos, gui));
122 | }
123 |
124 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final GuiScreen gui) {
125 | ifSizePresent(t -> t.render(customTexRes, renderPos, gui));
126 | }
127 |
128 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final WidgetGui gui, final Consumer before) {
129 | ifSizePresent(t -> before.andThen(t1 -> t.render(customTexRes, renderPos, gui)).accept(t));
130 | }
131 |
132 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final GuiScreen gui, final Consumer before) {
133 | ifSizePresent(t -> before.andThen(t1 -> t.render(customTexRes, renderPos, gui)).accept(t));
134 | }
135 |
136 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final WidgetGui gui, @Nullable final Consumer before, @Nullable final Consumer after) {
137 | ifPresent(t -> {
138 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
139 | t.render(customTexRes, renderPos, gui);
140 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
141 | });
142 | }
143 |
144 | public void renderIfPresent(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final GuiScreen gui, @Nullable final Consumer before, @Nullable final Consumer after) {
145 | ifPresent(t -> {
146 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
147 | t.render(customTexRes, renderPos, gui);
148 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
149 | });
150 | }
151 |
152 | public void ifSizePresent(final Consumer runnable) {
153 | if (width > 0 && height > 0) {
154 | runnable.accept(this);
155 | }
156 | }
157 |
158 | // Custom texture and size render
159 |
160 | public void render(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final RenderSize renderSize, final WidgetGui gui) {
161 | render(customTexRes, renderPos, renderSize, gui.getGui());
162 | }
163 |
164 | public void render(@Nullable final ResourceLocation customTexRes, final RenderPos renderPos, final RenderSize renderSize, final GuiScreen gui) {
165 | TextureManager textureManager = gui.mc.getTextureManager();
166 | if (texRes != null) {
167 | bind(textureManager);
168 | } else {
169 | if (customTexRes != null) {
170 | bind(textureManager, customTexRes);
171 | } else {
172 | return;
173 | }
174 | }
175 | gui.drawTexturedModalRect(renderPos.posX(), renderPos.posY(), texX, texY, renderSize.width(), renderSize.height());
176 | }
177 |
178 | // Custom size render
179 |
180 | public void render(final RenderPos renderPos, final RenderSize renderSize, final WidgetGui gui) {
181 | render(renderPos, renderSize, gui.getGui());
182 | }
183 |
184 | public void render(final RenderPos renderPos, final RenderSize renderSize, final GuiScreen gui) {
185 | bind(gui.mc.getTextureManager());
186 | gui.drawTexturedModalRect(renderPos.posX(), renderPos.posY(), texX, texY, renderSize.width(), renderSize.height());
187 | }
188 |
189 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final WidgetGui gui) {
190 | ifTexPresent(t -> t.render(renderPos, renderSize, gui));
191 | }
192 |
193 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final GuiScreen gui) {
194 | ifTexPresent(t -> t.render(renderPos, renderSize, gui));
195 | }
196 |
197 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final WidgetGui gui, final Consumer before) {
198 | ifTexPresent(t -> before.andThen(t1 -> t.render(renderPos, renderSize, gui)).accept(t));
199 | }
200 |
201 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final GuiScreen gui, final Consumer before) {
202 | ifTexPresent(t -> before.andThen(t1 -> t.render(renderPos, renderSize, gui)).accept(t));
203 | }
204 |
205 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final WidgetGui gui, @Nullable final Consumer before, @Nullable final Consumer after) {
206 | ifPresent(t -> {
207 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
208 | t.render(renderPos, renderSize, gui);
209 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
210 | });
211 | }
212 |
213 | public void renderIfPresent(final RenderPos renderPos, final RenderSize renderSize, final GuiScreen gui, @Nullable final Consumer before, @Nullable final Consumer after) {
214 | ifPresent(t -> {
215 | Optional.ofNullable(before).ifPresent(bc -> bc.accept(t));
216 | t.render(renderPos, renderSize, gui);
217 | Optional.ofNullable(after).ifPresent(ac -> ac.accept(t));
218 | });
219 | }
220 |
221 | public void ifTexPresent(final Consumer runnable) {
222 | if (texRes != null) {
223 | runnable.accept(this);
224 | }
225 | }
226 |
227 | }
228 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/Button.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
8 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
9 | import net.minecraft.util.ResourceLocation;
10 |
11 | import java.util.List;
12 | import java.util.Optional;
13 | import java.util.function.Consumer;
14 | import java.util.function.Function;
15 |
16 | /**
17 | * A button component with three states: Normal / Hovered / Unavailable.
18 | */
19 | @SuppressWarnings("unused")
20 | public class Button extends DynamicWidget {
21 |
22 | protected ResourceLocation textureLocation = null;
23 | protected TextureProperties texture = TextureProperties.EMPTY;
24 | protected TextureProperties hoveredTexture = TextureProperties.EMPTY;
25 | protected TextureProperties unavailableTexture= TextureProperties.EMPTY;
26 |
27 | protected boolean available = true;
28 |
29 | protected Consumer onClickedListener = null;
30 | protected Function> tooltipFunction = null;
31 |
32 | @Override
33 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
34 | if (!isVisible()) {
35 | return;
36 | }
37 | if (isUnavailable()) {
38 | unavailableTexture.render(textureLocation, renderPos, renderSize, gui);
39 | return;
40 | }
41 | if (isMouseOver(mousePos)) {
42 | hoveredTexture.render(textureLocation, renderPos, renderSize, gui);
43 | return;
44 | }
45 | texture.render(textureLocation, renderPos, renderSize, gui);
46 | }
47 |
48 | @Override
49 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
50 | if (isVisible() && onClickedListener != null && mouseButton == 0) {
51 | onClickedListener.accept(this);
52 | return true;
53 | }
54 | return false;
55 | }
56 |
57 | // Tooltips
58 |
59 | @Override
60 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
61 | if (available && tooltipFunction != null) {
62 | return tooltipFunction.apply(this);
63 | }
64 | return super.getHoverTooltips(widgetGui, mousePos);
65 | }
66 |
67 | // Getter Setter
68 |
69 | public ResourceLocation getTextureLocation() {
70 | return textureLocation;
71 | }
72 |
73 | public Button setTextureLocation(final ResourceLocation textureLocation) {
74 | this.textureLocation = textureLocation;
75 | return this;
76 | }
77 |
78 | public Button setTexture(final int textureX, final int textureY) {
79 | return setTexture(TextureProperties.of(textureX, textureY));
80 | }
81 |
82 | public Button setHoveredTexture(final int hoveredTextureX, final int hoveredTextureY) {
83 | return setHoveredTexture(TextureProperties.of(hoveredTextureX, hoveredTextureY));
84 | }
85 |
86 | public Button setUnavailableTexture(final int unavailableTextureX, final int unavailableTextureY) {
87 | return setUnavailableTexture(TextureProperties.of(unavailableTextureX, unavailableTextureY));
88 | }
89 |
90 | public TextureProperties getTexture() {
91 | return texture;
92 | }
93 |
94 | public Button setTexture(final TextureProperties texture) {
95 | this.texture = Optional.ofNullable(texture).orElse(TextureProperties.EMPTY);
96 | return this;
97 | }
98 |
99 | public TextureProperties getHoveredTexture() {
100 | return hoveredTexture;
101 | }
102 |
103 | public Button setHoveredTexture(final TextureProperties hoveredTexture) {
104 | this.hoveredTexture = Optional.ofNullable(hoveredTexture).orElse(TextureProperties.EMPTY);
105 | return this;
106 | }
107 |
108 | public TextureProperties getUnavailableTexture() {
109 | return unavailableTexture;
110 | }
111 |
112 | public Button setUnavailableTexture(final TextureProperties unavailableTexture) {
113 | this.unavailableTexture = Optional.ofNullable(unavailableTexture).orElse(TextureProperties.EMPTY);
114 | return this;
115 | }
116 |
117 | public boolean isAvailable() {
118 | return available;
119 | }
120 |
121 | public boolean isUnavailable() {
122 | return !available;
123 | }
124 |
125 | public Button setAvailable(final boolean available) {
126 | this.available = available;
127 | return this;
128 | }
129 |
130 | public Button setAvailable() {
131 | this.available = true;
132 | return this;
133 | }
134 |
135 | public Button setUnavailable() {
136 | this.available = false;
137 | return this;
138 | }
139 |
140 | public Consumer getOnClickedListener() {
141 | return onClickedListener;
142 | }
143 |
144 | public Button setOnClickedListener(final Consumer onClickedListener) {
145 | this.onClickedListener = onClickedListener;
146 | return this;
147 | }
148 |
149 | public Function> getTooltipFunction() {
150 | return tooltipFunction;
151 | }
152 |
153 | public Button setTooltipFunction(final Function> tooltipFunction) {
154 | this.tooltipFunction = tooltipFunction;
155 | return this;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/Button4State.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
8 |
9 | import java.util.Collections;
10 | import java.util.List;
11 | import java.util.Optional;
12 |
13 | /**
14 | * Inherits the normal button with an additional press state.
15 | */
16 | @SuppressWarnings("unused")
17 | public class Button4State extends Button {
18 |
19 | protected TextureProperties mouseDownTexture = TextureProperties.EMPTY;
20 |
21 | protected boolean mouseDown = false;
22 |
23 | @Override
24 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
25 | if (!isVisible()) {
26 | return;
27 | }
28 | if (isUnavailable()) {
29 | unavailableTexture.render(textureLocation, renderPos, renderSize, gui);
30 | return;
31 | }
32 | if (mouseDown) {
33 | mouseDownTexture.render(textureLocation, renderPos, renderSize, gui);
34 | return;
35 | }
36 | if (isMouseOver(mousePos)) {
37 | hoveredTexture.render(textureLocation, renderPos, renderSize, gui);
38 | return;
39 | }
40 | texture.render(textureLocation, renderPos, renderSize, gui);
41 | }
42 |
43 | @Override
44 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
45 | if (isVisible() && isAvailable() && mouseButton == 0) {
46 | return mouseDown = true;
47 | }
48 | return super.onMouseClick(mousePos, renderPos, mouseButton);
49 | }
50 |
51 | @Override
52 | public boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos) {
53 | if (isVisible() && isMouseOver(mousePos) && mouseDown) {
54 | mouseDown = false;
55 | if (onClickedListener != null) {
56 | onClickedListener.accept(this);
57 | }
58 | return true;
59 | }
60 | mouseDown = false;
61 | return false;
62 | }
63 |
64 | @Override
65 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
66 | if (mouseDown) {
67 | return Collections.emptyList();
68 | }
69 | return super.getHoverTooltips(widgetGui, mousePos);
70 | }
71 |
72 | public Button4State setMouseDownTexture(final int mouseDownTextureX, final int mouseDownTextureY) {
73 | return setMouseDownTexture(TextureProperties.of(mouseDownTextureX, mouseDownTextureY));
74 | }
75 |
76 | public TextureProperties getMouseDownTexture() {
77 | return mouseDownTexture;
78 | }
79 |
80 | public Button4State setMouseDownTexture(final TextureProperties mouseDownTexture) {
81 | this.mouseDownTexture = Optional.ofNullable(mouseDownTexture).orElse(TextureProperties.EMPTY);
82 | return this;
83 | }
84 |
85 | public boolean isMouseDown() {
86 | return mouseDown;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/Button5State.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
8 |
9 | import java.util.Optional;
10 |
11 | /**
12 | * Inherits the 4 states of the button with the addition of a persistent press state.
13 | */
14 | @SuppressWarnings("unused")
15 | public class Button5State extends Button4State {
16 |
17 | protected TextureProperties clickedTexture = TextureProperties.EMPTY;
18 |
19 | protected boolean clicked = false;
20 |
21 | @Override
22 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
23 | if (!isVisible()) {
24 | return;
25 | }
26 | if (isUnavailable()) {
27 | unavailableTexture.render(textureLocation, renderPos, renderSize, gui);
28 | return;
29 | }
30 | if (clicked) {
31 | clickedTexture.render(textureLocation, renderPos, renderSize, gui);
32 | return;
33 | }
34 | if (mouseDown) {
35 | mouseDownTexture.render(textureLocation, renderPos, renderSize, gui);
36 | return;
37 | }
38 | if (isMouseOver(mousePos)) {
39 | hoveredTexture.render(textureLocation, renderPos, renderSize, gui);
40 | return;
41 | }
42 | texture.render(textureLocation, renderPos, renderSize, gui);
43 | }
44 |
45 | @Override
46 | public boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos) {
47 | if (isVisible() && isMouseOver(mousePos) && mouseDown) {
48 | mouseDown = false;
49 | clicked = !clicked;
50 | if (onClickedListener != null) {
51 | onClickedListener.accept(this);
52 | }
53 | return true;
54 | }
55 | mouseDown = false;
56 | return false;
57 | }
58 |
59 | public Button5State setClickedTexture(final int clickedTextureX, final int clickedTextureY) {
60 | return setClickedTexture(TextureProperties.of(clickedTextureX, clickedTextureY));
61 | }
62 |
63 | public TextureProperties getClickedTexture() {
64 | return clickedTexture;
65 | }
66 |
67 | public Button5State setClickedTexture(final TextureProperties clickedTexture) {
68 | this.clickedTexture = Optional.ofNullable(clickedTexture).orElse(TextureProperties.EMPTY);;
69 | return this;
70 | }
71 |
72 | public boolean isClicked() {
73 | return clicked;
74 | }
75 |
76 | public Button5State setClicked(final boolean clicked) {
77 | this.clicked = clicked;
78 | return this;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/MultiLineLabel.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
8 | import net.minecraft.client.Minecraft;
9 | import net.minecraft.client.gui.FontRenderer;
10 | import net.minecraft.client.renderer.GlStateManager;
11 |
12 | import java.util.LinkedList;
13 | import java.util.List;
14 |
15 | public class MultiLineLabel extends DynamicWidget {
16 | public static final int DEFAULT_FONT_HEIGHT = 10;
17 |
18 | protected List contents;
19 |
20 | protected boolean leftAligned = true;
21 | protected boolean rightAligned = false;
22 | protected boolean verticalCentering = false;
23 | protected boolean autoRecalculateSize = true;
24 | protected boolean autoWrap = true;
25 |
26 | protected float scale = 1.0F;
27 |
28 | public MultiLineLabel(List contents) {
29 | this.contents = contents;
30 | this.width = Math.round(getMaxStringWidth());
31 | this.height = Math.round(getTotalHeight());
32 | setMargin(2);
33 | }
34 |
35 | @Override
36 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
37 | final float scale = this.scale;
38 | if (scale != 1F) {
39 | GlStateManager.pushMatrix();
40 | GlStateManager.scale(scale, scale, scale);
41 | }
42 |
43 | int maxWidth = (renderSize.isWidthLimited() && autoWrap) ? Math.round(Math.max((float) renderSize.width() / scale, DEFAULT_FONT_HEIGHT / scale)) : -1;
44 | int maxHeight = renderSize.isHeightLimited() ? Math.round((float) renderSize.height() / scale) : Math.round(height / scale);
45 | float totalHeight = getTotalHeight();
46 | float posX = renderPos.posX() / scale;
47 | float posY = renderPos.posY() / scale + (verticalCentering ? (maxHeight - totalHeight) / 2F : 0);
48 |
49 | FontRenderer fr = gui.getGui().mc.fontRenderer;
50 |
51 | List toRender;
52 | if (maxWidth == -1) {
53 | toRender = contents;
54 | } else {
55 | toRender = new LinkedList<>();
56 | for (String s : contents) {
57 | toRender.addAll(fr.listFormattedStringToWidth(s, maxWidth));
58 | }
59 | }
60 |
61 | float offsetY = 0;
62 | float fontHeight = DEFAULT_FONT_HEIGHT * scale;
63 | for (final String s : toRender) {
64 | fr.drawStringWithShadow(s, posX + getLineRenderOffset(s, fr), posY + offsetY, 0xFFFFFF);
65 | offsetY += fontHeight;
66 | if (offsetY > maxHeight + fontHeight) {
67 | break;
68 | }
69 | }
70 |
71 | if (scale != 1F) {
72 | GlStateManager.popMatrix();
73 | }
74 |
75 | GlStateManager.color(1.0F, 1.0F, 1.0F);
76 | }
77 |
78 | // Utils
79 |
80 | protected float getLineRenderOffset(final String s, final FontRenderer fr) {
81 | if (leftAligned && !rightAligned) {
82 | return 0;
83 | }
84 |
85 | int width = getWidth();
86 | float stringWidth = fr.getStringWidth(s) * scale;
87 |
88 | if (leftAligned && rightAligned) {
89 | return (width - (stringWidth)) / 2F;
90 | } else if (rightAligned) {
91 | return (width - (stringWidth));
92 | } else {
93 | return (width - (stringWidth)) / 2F;
94 | }
95 | }
96 |
97 | public float getMaxStringWidth() {
98 | FontRenderer fr = Minecraft.getMinecraft().fontRenderer;
99 |
100 | float maxWidth = 0;
101 | for (final String content : contents) {
102 | float width = fr.getStringWidth(content) * scale;
103 | if (width > maxWidth) {
104 | maxWidth = width;
105 | }
106 | }
107 |
108 | return maxWidth;
109 | }
110 |
111 | public float getTotalHeight() {
112 | return getTotalHeight(contents);
113 | }
114 |
115 | public float getTotalHeight(List contents) {
116 | FontRenderer fr = Minecraft.getMinecraft().fontRenderer;
117 |
118 | float height = 0;
119 | for (final String content : contents) {
120 | List listed = fr.listFormattedStringToWidth(content, Math.round(width / scale));
121 | height += (float) DEFAULT_FONT_HEIGHT * scale * listed.size();
122 | }
123 |
124 | return height * scale;
125 | }
126 |
127 | // Getters / Setters
128 |
129 | public float getScale() {
130 | return scale;
131 | }
132 |
133 | public MultiLineLabel setScale(final float scale) {
134 | this.scale = scale;
135 | this.height = Math.round(getTotalHeight());
136 | return this;
137 | }
138 |
139 | @Override
140 | public MultiLineLabel setWidth(final int width) {
141 | super.setWidth(width);
142 | this.height = Math.round(getTotalHeight());
143 | return this;
144 | }
145 |
146 | public List getContents() {
147 | return contents;
148 | }
149 |
150 | public MultiLineLabel setContents(final List contents) {
151 | this.contents = contents;
152 | if (autoRecalculateSize) {
153 | this.width = Math.round(getMaxStringWidth());
154 | this.height = Math.round(getTotalHeight());
155 | }
156 | return this;
157 | }
158 |
159 | // Align
160 |
161 | public boolean isLeftAligned() {
162 | return leftAligned;
163 | }
164 |
165 | public MultiLineLabel setLeftAligned(final boolean leftAligned) {
166 | this.rightAligned = !leftAligned;
167 | this.leftAligned = leftAligned;
168 | return this;
169 | }
170 |
171 | public boolean isRightAligned() {
172 | return rightAligned;
173 | }
174 |
175 | public MultiLineLabel setRightAligned(final boolean rightAligned) {
176 | this.leftAligned = !rightAligned;
177 | this.rightAligned = rightAligned;
178 | return this;
179 | }
180 |
181 | public boolean isCenterAligned() {
182 | return this.leftAligned && this.rightAligned;
183 | }
184 |
185 | public MultiLineLabel setCenterAligned(final boolean centerAligned) {
186 | if (centerAligned) {
187 | this.leftAligned = true;
188 | this.rightAligned = true;
189 | return this;
190 | }
191 | // Default setting is left aligned.
192 | this.leftAligned = true;
193 | this.rightAligned = false;
194 | return this;
195 | }
196 |
197 | public boolean isVerticalCentering() {
198 | return verticalCentering;
199 | }
200 |
201 | public MultiLineLabel setVerticalCentering(final boolean verticalCentering) {
202 | this.verticalCentering = verticalCentering;
203 | return this;
204 | }
205 |
206 | // Auto recalculate size
207 |
208 | public boolean isAutoRecalculateSize() {
209 | return autoRecalculateSize;
210 | }
211 |
212 | public MultiLineLabel setAutoRecalculateSize(final boolean autoRecalculateSize) {
213 | this.autoRecalculateSize = autoRecalculateSize;
214 | return this;
215 | }
216 |
217 | // Auto wrap
218 |
219 | public boolean isAutoWrap() {
220 | return autoWrap;
221 | }
222 |
223 | public MultiLineLabel setAutoWrap(final boolean autoWrap) {
224 | this.autoWrap = autoWrap;
225 | return this;
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/Scrollbar.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
8 |
9 | import java.util.List;
10 | import java.util.function.Consumer;
11 |
12 | @SuppressWarnings("unused")
13 | public class Scrollbar extends DynamicWidget {
14 | public static final int DEFAULT_SCROLL_WIDTH = 6;
15 | public static final int DEFAULT_SCROLL_HEIGHT = 27;
16 |
17 | public static final int DEFAULT_TEXTURE_X = 0;
18 | public static final int DEFAULT_TEXTURE_Y = 22;
19 |
20 | public static final int DEFAULT_TEXTURE_OFFSET_X = 6;
21 | public static final int DEFAULT_TEXTURE_OFFSET_Y = 0;
22 |
23 | public static final int DEFAULT_SCROLL_UNIT = 1;
24 |
25 | protected final Button4State scroll = new Button4State();
26 |
27 | protected int maxScroll = 0;
28 | protected int minScroll = 0;
29 | protected int currentScroll = 0;
30 |
31 | protected int scrollUnit = DEFAULT_SCROLL_UNIT;
32 |
33 | protected boolean mouseDown = false;
34 |
35 | protected boolean mouseWheelCheckPos = true;
36 |
37 | protected Consumer onValueChanged = null;
38 |
39 | public Scrollbar() {
40 | this.width = scroll.getWidth();
41 | this.height = scroll.getHeight() * 2;
42 | }
43 |
44 | @Override
45 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
46 | int height = renderSize.isHeightLimited() ? renderSize.height() : this.height;
47 | int scrollHeight = scroll.getHeight();
48 | int scrollWidth = scroll.getWidth();
49 |
50 | if (this.getRange() == 0) {
51 | scroll.render(gui, new RenderSize(scrollWidth, scrollHeight), renderPos, mousePos);
52 | } else {
53 | int offsetY = (this.currentScroll - this.minScroll) * (height - scrollHeight) / this.getRange();
54 | scroll.render(gui,
55 | new RenderSize(scrollWidth, scrollHeight),
56 | renderPos.add(new RenderPos(0, offsetY)),
57 | mousePos.relativeTo(new RenderPos(0, offsetY)));
58 | }
59 | }
60 |
61 | @Override
62 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
63 | if (this.getRange() == 0 || mouseButton != 0) {
64 | return false;
65 | }
66 |
67 | int scrollHeight = scroll.getHeight();
68 | int offsetY = getRange() == 0 ? 0 : (this.currentScroll - this.minScroll) * (height - scrollHeight) / this.getRange();
69 | RenderPos offset = new RenderPos(0, offsetY);
70 | MousePos scrollMousePos = mousePos.relativeTo(offset);
71 | if (scroll.isMouseOver(scrollMousePos) && scroll.onMouseClick(mousePos, renderPos.subtract(offset), mouseButton)) {
72 | return mouseDown = true;
73 | }
74 |
75 | return false;
76 | }
77 |
78 | @Override
79 | public boolean onMouseClickMove(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
80 | if (mouseDown) {
81 | handleMouseDragMove(mousePos);
82 | }
83 | return super.onMouseClickMove(mousePos, renderPos, mouseButton);
84 | }
85 |
86 | protected void handleMouseDragMove(final MousePos mousePos) {
87 | float clickedPercent = Math.min(Math.max((float) mousePos.mouseY() / this.height, 0), 1F);
88 | int scroll = Math.round((float) getRange() * clickedPercent);
89 | setCurrentScroll(scroll + this.minScroll);
90 | }
91 |
92 | @Override
93 | public boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos) {
94 | int scrollHeight = scroll.getHeight();
95 | int offsetY = getRange() == 0 ? 0 : (this.currentScroll - this.minScroll) * (height - scrollHeight) / this.getRange();
96 | RenderPos offset = new RenderPos(0, offsetY);
97 | MousePos scrollMousePos = mousePos.relativeTo(offset);
98 | scroll.onMouseReleased(mousePos, renderPos.subtract(offset));
99 |
100 | mouseDown = false;
101 | return false;
102 | }
103 |
104 | @Override
105 | public boolean onMouseDWheel(final MousePos mousePos, final RenderPos renderPos, final int dWheel) {
106 | if (mouseWheelCheckPos) {
107 | if (!isMouseOver(mousePos)) {
108 | return false;
109 | }
110 | }
111 | int wheel = Math.max(Math.min(-dWheel, 1), -1);
112 | setCurrentScroll(this.currentScroll + (wheel * this.scrollUnit));
113 | return true;
114 | }
115 |
116 | @Override
117 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
118 | int scrollHeight = scroll.getHeight();
119 | int offsetY = getRange() == 0 ? 0 : (this.currentScroll - this.minScroll) * (height - scrollHeight) / this.getRange();
120 | RenderPos offset = new RenderPos(0, offsetY);
121 | MousePos scrollMousePos = mousePos.relativeTo(offset);
122 | if (scroll.isMouseOver(scrollMousePos)) {
123 | return scroll.getHoverTooltips(widgetGui, mousePos);
124 | }
125 |
126 | return super.getHoverTooltips(widgetGui, mousePos);
127 | }
128 |
129 | // Scroll button
130 |
131 | public Button4State getScroll() {
132 | return scroll;
133 | }
134 |
135 | // Scroll Range
136 |
137 | public int getMaxScroll() {
138 | return maxScroll;
139 | }
140 |
141 | public int getMinScroll() {
142 | return minScroll;
143 | }
144 |
145 | public int getCurrentScroll() {
146 | return currentScroll;
147 | }
148 |
149 | public Scrollbar setCurrentScroll(final int currentScroll) {
150 | this.currentScroll = Math.max(Math.min(currentScroll, this.maxScroll), this.minScroll);
151 | if (onValueChanged != null) {
152 | onValueChanged.accept(this);
153 | }
154 | return this;
155 | }
156 |
157 | public int getRange() {
158 | return this.maxScroll - this.minScroll;
159 | }
160 |
161 | public Scrollbar setRange(final int min, final int max) {
162 | this.minScroll = min;
163 | this.maxScroll = max;
164 |
165 | if (this.minScroll > this.maxScroll) {
166 | this.maxScroll = this.minScroll;
167 | }
168 | this.scroll.setAvailable(getRange() != 0);
169 |
170 | setCurrentScroll(this.currentScroll);
171 | return this;
172 | }
173 |
174 | // Scroll Unit
175 |
176 | public int getScrollUnit() {
177 | return scrollUnit;
178 | }
179 |
180 | public Scrollbar setScrollUnit(final int scrollUnit) {
181 | this.scrollUnit = Math.max(scrollUnit, 1);
182 | return this;
183 | }
184 |
185 | // Handlers
186 |
187 | public Consumer getOnValueChanged() {
188 | return onValueChanged;
189 | }
190 |
191 | public Scrollbar setOnValueChanged(final Consumer onValueChanged) {
192 | this.onValueChanged = onValueChanged;
193 | return this;
194 | }
195 |
196 | // Handle mouse wheel
197 |
198 | public boolean isMouseWheelCheckPos() {
199 | return mouseWheelCheckPos;
200 | }
201 |
202 | public Scrollbar setMouseWheelCheckPos(final boolean mouseWheelCheckPos) {
203 | this.mouseWheelCheckPos = mouseWheelCheckPos;
204 | return this;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/SizedColumn.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
4 | import github.kasuminova.ae2ctl.client.gui.widget.container.Column;
5 |
6 | public class SizedColumn extends Column {
7 |
8 | @Override
9 | public int getHeight() {
10 | return this.height;
11 | }
12 |
13 | @Override
14 | public DynamicWidget setHeight(final int height) {
15 | this.height = height;
16 | return this;
17 | }
18 |
19 | @Override
20 | public int getWidth() {
21 | return this.width;
22 | }
23 |
24 | @Override
25 | public DynamicWidget setWidth(final int width) {
26 | this.width = width;
27 | return this;
28 | }
29 |
30 | @Override
31 | public DynamicWidget setWidthHeight(final int width, final int height) {
32 | return setWidth(width).setHeight(height);
33 | }
34 |
35 | public int getTotalWidth() {
36 | return super.getWidth();
37 | }
38 |
39 | public int getTotalHeight() {
40 | return super.getHeight();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/SizedRow.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
4 | import github.kasuminova.ae2ctl.client.gui.widget.container.Row;
5 |
6 | public class SizedRow extends Row {
7 |
8 | @Override
9 | public int getHeight() {
10 | return this.height;
11 | }
12 |
13 | @Override
14 | public DynamicWidget setHeight(final int height) {
15 | this.height = height;
16 | return this;
17 | }
18 |
19 | @Override
20 | public int getWidth() {
21 | return this.width;
22 | }
23 |
24 | @Override
25 | public DynamicWidget setWidth(final int width) {
26 | this.width = width;
27 | return this;
28 | }
29 |
30 | @Override
31 | public DynamicWidget setWidthHeight(final int width, final int height) {
32 | return setWidth(width).setHeight(height);
33 | }
34 |
35 | public int getTotalWidth() {
36 | return super.getWidth();
37 | }
38 |
39 | public int getTotalHeight() {
40 | return super.getHeight();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/base/WidgetController.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.base;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.widget.container.WidgetContainer;
7 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
8 | import net.minecraft.client.renderer.GlStateManager;
9 | import org.lwjgl.input.Mouse;
10 |
11 | import javax.annotation.Nonnull;
12 | import java.util.ArrayList;
13 | import java.util.Collections;
14 | import java.util.List;
15 | import java.util.stream.Collectors;
16 |
17 | /**
18 | * 一个轻量化的组件库核心类,用来控制所有的组件,可以被附加到任意 GUI 上(只要你能调用它的所有方法)。
19 | *
20 | *
21 | * A lightweight component library core class, used to control all the components,
22 | * can be attached to any GUI (as long as you can call it's all methods).
23 | */
24 | public class WidgetController {
25 |
26 | public static final ThreadLocal TRANSLATE_STATE = ThreadLocal.withInitial(() -> new RenderPos(0, 0));
27 |
28 | protected final WidgetGui gui;
29 | protected final List widgets = new ArrayList<>();
30 |
31 | protected List tooltipCache = new ArrayList<>();
32 |
33 | private boolean initialized = false;
34 |
35 | public WidgetController(final WidgetGui gui) {
36 | this.gui = gui;
37 | }
38 |
39 | @Deprecated
40 | public void addWidgetContainer(final WidgetContainer widgetContainer) {
41 | widgets.add(widgetContainer);
42 | }
43 |
44 | public void addWidget(final DynamicWidget widget) {
45 | widgets.add(widget);
46 | }
47 |
48 | public void render(final MousePos mousePos, final boolean translatePos) {
49 | tooltipCache = getHoverTooltipsInternal(mousePos);
50 |
51 | WidgetGui gui = this.gui;
52 |
53 | final int guiLeft = gui.getGuiLeft();
54 | final int guiTop = gui.getGuiTop();
55 | RenderPos offset = new RenderPos(guiLeft, guiTop);
56 |
57 | GlStateManager.pushMatrix();
58 | if (translatePos) {
59 | GlStateManager.translate(guiLeft, guiTop, 0F);
60 | TRANSLATE_STATE.set(TRANSLATE_STATE.get().add(offset));
61 | }
62 |
63 | for (final DynamicWidget container : widgets) {
64 | RenderPos renderPos = new RenderPos(guiLeft + container.getAbsX(), guiTop + container.getAbsY());
65 | RenderPos relativeRenderPos = renderPos.subtract(offset);
66 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
67 | RenderSize renderSize = new RenderSize(container.getWidth(), container.getHeight());
68 | container.preRender(gui, renderSize, relativeRenderPos, relativeMousePos);
69 | }
70 | for (final DynamicWidget container : widgets) {
71 | RenderPos renderPos = new RenderPos(guiLeft + container.getAbsX(), guiTop + container.getAbsY());
72 | RenderPos relativeRenderPos = renderPos.subtract(offset);
73 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
74 | RenderSize renderSize = new RenderSize(container.getWidth(), container.getHeight());
75 | container.render(gui, renderSize, relativeRenderPos, relativeMousePos);
76 | }
77 |
78 | if (translatePos) {
79 | TRANSLATE_STATE.set(TRANSLATE_STATE.get().subtract(offset));
80 | }
81 | GlStateManager.popMatrix();
82 | }
83 |
84 | public void postRender(final MousePos mousePos, final boolean translatePos) {
85 | WidgetGui gui = this.gui;
86 |
87 | final int guiLeft = gui.getGuiLeft();
88 | final int guiTop = gui.getGuiTop();
89 | RenderPos offset = new RenderPos(guiLeft, guiTop);
90 |
91 | GlStateManager.pushMatrix();
92 | if (translatePos) {
93 | GlStateManager.translate(guiLeft, guiTop, 0F);
94 | TRANSLATE_STATE.set(TRANSLATE_STATE.get().add(offset));
95 | }
96 |
97 | for (final DynamicWidget container : widgets) {
98 | RenderPos renderPos = new RenderPos(guiLeft + container.getAbsX(), guiTop + container.getAbsY());
99 | RenderPos relativeRenderPos = renderPos.subtract(new RenderPos(guiLeft, guiTop));
100 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
101 | RenderSize renderSize = new RenderSize(container.getWidth(), container.getHeight());
102 | container.postRender(gui, renderSize, relativeRenderPos, relativeMousePos);
103 | }
104 |
105 | if (translatePos) {
106 | TRANSLATE_STATE.set(TRANSLATE_STATE.get().subtract(offset));
107 | }
108 | GlStateManager.popMatrix();
109 | }
110 |
111 | public void renderTooltip(final MousePos mousePos) {
112 | final int guiLeft = gui.getGuiLeft();
113 | final int guiTop = gui.getGuiTop();
114 |
115 | List hoverTooltips = getHoverTooltips(mousePos);
116 | if (!hoverTooltips.isEmpty()) {
117 | MousePos relativeMousePos = mousePos.relativeTo(new RenderPos(guiLeft, guiTop));
118 | gui.getGui().drawHoveringText(hoverTooltips, relativeMousePos.mouseX(), relativeMousePos.mouseY());
119 | }
120 | }
121 |
122 | public void init() {
123 | if (!initialized) {
124 | WidgetGui gui = this.gui;
125 | widgets.forEach(container -> container.initWidget(gui));
126 | }
127 | this.initialized = true;
128 | }
129 |
130 | public void update() {
131 | WidgetGui gui = this.gui;
132 | widgets.forEach(container -> container.update(gui));
133 | }
134 |
135 | public void onGUIClosed() {
136 | WidgetGui gui = this.gui;
137 | widgets.forEach(container -> container.onGUIClosed(gui));
138 | }
139 |
140 | public void postGuiEvent(GuiEvent event) {
141 | for (final DynamicWidget container : widgets) {
142 | if (container.onGuiEvent(event)) {
143 | break;
144 | }
145 | }
146 | }
147 |
148 | public boolean onMouseClicked(final MousePos mousePos, final int mouseButton) {
149 | WidgetGui gui = this.gui;
150 |
151 | final int x = gui.getGuiLeft();
152 | final int y = gui.getGuiTop();
153 |
154 | boolean mouseClickEventCancelled = false;
155 |
156 | for (final DynamicWidget container : widgets) {
157 | RenderPos renderPos = new RenderPos(x + container.getAbsX(), y + container.getAbsY());
158 | RenderPos relativeRenderPos = renderPos.subtract(new RenderPos(x, y));
159 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
160 |
161 | container.onMouseClickGlobal(relativeMousePos, relativeRenderPos, mouseButton);
162 | if (!mouseClickEventCancelled && container.isMouseOver(relativeMousePos)) {
163 | if (container.onMouseClick(relativeMousePos, relativeRenderPos, mouseButton)) {
164 | mouseClickEventCancelled = true;
165 | }
166 | }
167 | }
168 |
169 | return mouseClickEventCancelled;
170 | }
171 |
172 | public boolean onMouseClickMove(final MousePos mousePos, final int mouseButton) {
173 | WidgetGui gui = this.gui;
174 |
175 | final int x = gui.getGuiLeft();
176 | final int y = gui.getGuiTop();
177 |
178 | for (final DynamicWidget container : widgets) {
179 | RenderPos renderPos = new RenderPos(x + container.getAbsX(), y + container.getAbsY());
180 | RenderPos relativeRenderPos = renderPos.subtract(new RenderPos(x, y));
181 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
182 |
183 | if (container.onMouseClickMove(relativeMousePos, relativeRenderPos, mouseButton)) {
184 | return true;
185 | }
186 | }
187 | return false;
188 | }
189 |
190 | public boolean onMouseReleased(final MousePos mousePos) {
191 | WidgetGui gui = this.gui;
192 |
193 | final int x = gui.getGuiLeft();
194 | final int y = gui.getGuiTop();
195 |
196 | for (final DynamicWidget container : widgets) {
197 | RenderPos renderPos = new RenderPos(x + container.getAbsX(), y + container.getAbsY());
198 | RenderPos relativeRenderPos = renderPos.subtract(new RenderPos(x, y));
199 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
200 |
201 | if (container.onMouseReleased(relativeMousePos, relativeRenderPos)) {
202 | return true;
203 | }
204 | }
205 | return false;
206 | }
207 |
208 | public boolean onMouseInput(final MousePos mousePos) {
209 | final int wheel = Mouse.getEventDWheel();
210 | if (wheel == 0) {
211 | return false;
212 | }
213 | WidgetGui gui = this.gui;
214 |
215 | final int x = gui.getGuiLeft();
216 | final int y = gui.getGuiTop();
217 |
218 | for (final DynamicWidget container : widgets) {
219 | RenderPos renderPos = new RenderPos(x + container.getAbsX(), y + container.getAbsY());
220 | RenderPos relativeRenderPos = renderPos.subtract(new RenderPos(x, y));
221 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
222 |
223 | if (container.onMouseDWheel(relativeMousePos, relativeRenderPos, wheel)) {
224 | return true;
225 | }
226 | }
227 | return false;
228 | }
229 |
230 | public boolean onKeyTyped(final char typedChar, final int keyCode) {
231 | for (final DynamicWidget container : widgets) {
232 | if (container.onKeyTyped(typedChar, keyCode)) {
233 | return true;
234 | }
235 | }
236 | return false;
237 | }
238 |
239 | public List getHoverTooltips(final MousePos mousePos) {
240 | return tooltipCache;
241 | }
242 |
243 | @Nonnull
244 | private List getHoverTooltipsInternal(final MousePos mousePos) {
245 | WidgetGui gui = this.gui;
246 |
247 | final int x = gui.getGuiLeft();
248 | final int y = gui.getGuiTop();
249 |
250 | List tooltips = null;
251 | for (final DynamicWidget container : widgets) {
252 | RenderPos renderPos = new RenderPos(x + container.getAbsX(), y + container.getAbsY());
253 | MousePos relativeMousePos = mousePos.relativeTo(renderPos);
254 |
255 | if (container.isMouseOver(relativeMousePos)) {
256 | List hoverTooltips = container.getHoverTooltips(this.gui, relativeMousePos);
257 | if (!hoverTooltips.isEmpty()) {
258 | tooltips = hoverTooltips;
259 | break;
260 | }
261 | }
262 | }
263 |
264 | return tooltips != null ? tooltips : Collections.emptyList();
265 | }
266 |
267 | public WidgetGui getGui() {
268 | return gui;
269 | }
270 |
271 | public List getWidgets() {
272 | return widgets;
273 | }
274 |
275 | @Deprecated
276 | public List getContainers() {
277 | return widgets.stream()
278 | .filter(WidgetContainer.class::isInstance)
279 | .map(WidgetContainer.class::cast)
280 | .collect(Collectors.toList());
281 | }
282 |
283 | }
284 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/base/WidgetGui.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.base;
2 |
3 | import net.minecraft.client.gui.GuiScreen;
4 | import net.minecraft.client.gui.inventory.GuiContainer;
5 |
6 | /**
7 | * 一个轻量化的组件库核心类,用来存储 GUI 的属性数据,被所有 {@link DynamicWidget} 使用。
8 | *
9 | *
10 | * A lightweight component library core class, used to store GUI properties and used by all {@link DynamicWidget}.
11 | */
12 | public class WidgetGui {
13 | private final GuiScreen gui;
14 | private int xSize;
15 | private int ySize;
16 | private int guiLeft;
17 | private int guiTop;
18 |
19 | public WidgetGui(final GuiScreen gui, final int xSize, final int ySize, final int guiLeft, final int guiTop) {
20 | this.gui = gui;
21 | this.xSize = xSize;
22 | this.ySize = ySize;
23 | this.guiLeft = guiLeft;
24 | this.guiTop = guiTop;
25 | }
26 |
27 | public static WidgetGui of(final GuiContainer container) {
28 | return new WidgetGui(container, container.getXSize(), container.getYSize(), container.getGuiLeft(), container.getGuiTop());
29 | }
30 |
31 | public static WidgetGui of(final GuiScreen guiScreen, final int xSize, final int ySize, final int guiLeft, final int guiTop) {
32 | return new WidgetGui(guiScreen, xSize, ySize, guiLeft, guiTop);
33 | }
34 |
35 | public GuiScreen getGui() {
36 | return gui;
37 | }
38 |
39 | public int getWidth() {
40 | return gui.width;
41 | }
42 |
43 | public int getHeight() {
44 | return gui.height;
45 | }
46 |
47 | public int getXSize() {
48 | return xSize;
49 | }
50 |
51 | public WidgetGui setXSize(final int xSize) {
52 | this.xSize = xSize;
53 | return this;
54 | }
55 |
56 | public int getYSize() {
57 | return ySize;
58 | }
59 |
60 | public WidgetGui setYSize(final int ySize) {
61 | this.ySize = ySize;
62 | return this;
63 | }
64 |
65 | public int getGuiLeft() {
66 | return guiLeft;
67 | }
68 |
69 | public WidgetGui setGuiLeft(final int guiLeft) {
70 | this.guiLeft = guiLeft;
71 | return this;
72 | }
73 |
74 | public int getGuiTop() {
75 | return guiTop;
76 | }
77 |
78 | public WidgetGui setGuiTop(final int guiTop) {
79 | this.guiTop = guiTop;
80 | return this;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/container/Column.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.container;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderFunction;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
6 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
8 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
9 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | @SuppressWarnings("unused")
16 | public class Column extends WidgetContainer {
17 | protected final List widgets = new ArrayList<>();
18 |
19 | protected boolean leftAligned = true;
20 | protected boolean rightAligned = false;
21 |
22 | @Override
23 | protected void preRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
24 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::preRender);
25 | }
26 |
27 | @Override
28 | protected void renderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
29 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::render);
30 | }
31 |
32 | @Override
33 | protected void postRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
34 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::postRender);
35 | }
36 |
37 | protected void doRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos,
38 | final RenderFunction renderFunction) {
39 | int y = 0;
40 |
41 | int width = getWidth();
42 |
43 | for (final DynamicWidget widget : widgets) {
44 | if (widget.isDisabled()) {
45 | continue;
46 | }
47 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
48 | if (widgetRenderPos == null) {
49 | continue;
50 | }
51 | if (widget.isVisible()) {
52 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
53 | renderFunction.doRender(widget, gui, new RenderSize(widget.getWidth(), widget.getHeight()).smaller(renderSize), absRenderPos, mousePos.relativeTo(widgetRenderPos));
54 | }
55 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
56 | }
57 | }
58 |
59 | @Override
60 | public List getWidgets() {
61 | return widgets;
62 | }
63 |
64 | @Override
65 | public Column addWidget(final DynamicWidget widget) {
66 | if (widget == this) {
67 | throw new IllegalArgumentException("Containers cannot be added to their own containers!");
68 | }
69 | widgets.add(widget);
70 | return this;
71 | }
72 |
73 | @Override
74 | public Column removeWidget(final DynamicWidget widget) {
75 | widgets.remove(widget);
76 | return this;
77 | }
78 |
79 | // GUI EventHandlers
80 |
81 | @Override
82 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
83 | int y = 0;
84 |
85 | int width = getWidth();
86 |
87 | for (final DynamicWidget widget : widgets) {
88 | if (widget.isDisabled()) {
89 | continue;
90 | }
91 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
92 | if (widgetRenderPos == null) {
93 | continue;
94 | }
95 | int offsetX = widgetRenderPos.posX();
96 | int offsetY = widgetRenderPos.posY();
97 |
98 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
99 | if (widget.isMouseOver(relativeMousePos)) {
100 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
101 | if (widget.onMouseClick(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton)) {
102 | return true;
103 | }
104 | }
105 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
106 | }
107 |
108 | return false;
109 | }
110 |
111 | @Override
112 | public void onMouseClickGlobal(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
113 | int y = 0;
114 |
115 | int width = getWidth();
116 |
117 | for (final DynamicWidget widget : widgets) {
118 | if (widget.isDisabled()) {
119 | continue;
120 | }
121 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
122 | if (widgetRenderPos == null) {
123 | continue;
124 | }
125 | int offsetX = widgetRenderPos.posX();
126 | int offsetY = widgetRenderPos.posY();
127 |
128 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
129 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
130 | widget.onMouseClickGlobal(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton);
131 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
132 | }
133 | }
134 |
135 | @Override
136 | public boolean onMouseClickMove(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
137 | int y = 0;
138 |
139 | int width = getWidth();
140 |
141 | for (final DynamicWidget widget : widgets) {
142 | if (widget.isDisabled()) {
143 | continue;
144 | }
145 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
146 | if (widgetRenderPos == null) {
147 | continue;
148 | }
149 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
150 | if (widget.onMouseClickMove(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton)) {
151 | return true;
152 | }
153 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
154 | }
155 | return false;
156 | }
157 |
158 | @Override
159 | public boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos) {
160 | int y = 0;
161 |
162 | int width = getWidth();
163 |
164 | for (final DynamicWidget widget : widgets) {
165 | if (widget.isDisabled()) {
166 | continue;
167 | }
168 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
169 | if (widgetRenderPos == null) {
170 | continue;
171 | }
172 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
173 | if (widget.onMouseReleased(mousePos.relativeTo(widgetRenderPos), absRenderPos)) {
174 | return true;
175 | }
176 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
177 | }
178 | return false;
179 | }
180 |
181 | @Override
182 | public boolean onMouseDWheel(final MousePos mousePos, final RenderPos renderPos, final int wheel) {
183 | int y = 0;
184 |
185 | int width = getWidth();
186 |
187 | for (final DynamicWidget widget : widgets) {
188 | if (widget.isDisabled()) {
189 | continue;
190 | }
191 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
192 | if (widgetRenderPos == null) {
193 | continue;
194 | }
195 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
196 | if (widget.onMouseDWheel(mousePos.relativeTo(widgetRenderPos), absRenderPos, wheel)) {
197 | return true;
198 | }
199 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
200 | }
201 | return false;
202 | }
203 |
204 | @Override
205 | public boolean onKeyTyped(final char typedChar, final int keyCode) {
206 | for (final DynamicWidget widget : widgets) {
207 | if (widget.isDisabled()) {
208 | continue;
209 | }
210 | if (widget.onKeyTyped(typedChar, keyCode)) {
211 | return true;
212 | }
213 | }
214 | return false;
215 | }
216 |
217 | // Tooltips
218 |
219 | @Override
220 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
221 | int y = 0;
222 |
223 | int width = getWidth();
224 |
225 | List tooltips = null;
226 |
227 | for (final DynamicWidget widget : widgets) {
228 | if (widget.isDisabled()) {
229 | continue;
230 | }
231 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, width, y);
232 | if (widgetRenderPos == null) {
233 | continue;
234 | }
235 | int offsetX = widgetRenderPos.posX();
236 | int offsetY = widgetRenderPos.posY();
237 |
238 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
239 | if (widget.isMouseOver(relativeMousePos)) {
240 | List hoverTooltips = widget.getHoverTooltips(widgetGui, relativeMousePos);
241 | if (!hoverTooltips.isEmpty()) {
242 | tooltips = hoverTooltips;
243 | break;
244 | }
245 | }
246 |
247 | y += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
248 | }
249 |
250 | return tooltips != null ? tooltips : Collections.emptyList();
251 | }
252 |
253 | // CustomEventHandlers
254 |
255 | @Override
256 | public boolean onGuiEvent(final GuiEvent event) {
257 | for (final DynamicWidget widget : widgets) {
258 | // if (widget.isDisabled()) {
259 | // continue;
260 | // }
261 | if (widget.onGuiEvent(event)) {
262 | return true;
263 | }
264 | }
265 | return false;
266 | }
267 |
268 | // Utils
269 |
270 | public RenderPos getWidgetRenderOffset(DynamicWidget widget, int width, int y) {
271 | int xOffset;
272 | int yOffset;
273 |
274 | if (widget.isUseAbsPos()) {
275 | xOffset = widget.getAbsX();
276 | yOffset = widget.getAbsY();
277 | } else if (isCenterAligned()) {
278 | xOffset = (width - (widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight())) / 2;
279 | yOffset = y + widget.getMarginUp();
280 | } else if (leftAligned) {
281 | xOffset = widget.getMarginLeft();
282 | yOffset = y + widget.getMarginUp();
283 | } else if (rightAligned) {
284 | xOffset = width - (widget.getWidth() + widget.getMarginRight());
285 | yOffset = y + widget.getMarginUp();
286 | } else {
287 | // Where does it align?
288 | return null;
289 | }
290 |
291 | return new RenderPos(xOffset, yOffset);
292 | }
293 |
294 | // X/Y Size
295 |
296 | @Override
297 | public int getWidth() {
298 | int maxX = 0;
299 | for (final DynamicWidget widget : widgets) {
300 | if (widget.isDisabled()) {
301 | continue;
302 | }
303 | int width = 0;
304 | if (widget.isUseAbsPos()) {
305 | width += widget.getAbsX();
306 | }
307 | width += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
308 | if (width > maxX) {
309 | maxX = width;
310 | }
311 | }
312 | return maxX;
313 | }
314 |
315 | @Override
316 | public DynamicWidget setWidth(final int width) {
317 | // It's dynamic, so ignore it.
318 | return this;
319 | }
320 |
321 | @Override
322 | public int getHeight() {
323 | int height = 0;
324 | int absWidgetMaxHeight = 0;
325 | for (final DynamicWidget widget : widgets) {
326 | if (widget.isDisabled()) {
327 | continue;
328 | }
329 | int widgetHeight = widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
330 | if (widget.isUseAbsPos()) {
331 | absWidgetMaxHeight = Math.max(widgetHeight + widget.getAbsY(), absWidgetMaxHeight);
332 | continue;
333 | }
334 | height += widgetHeight;
335 | }
336 | return Math.max(height, absWidgetMaxHeight);
337 | }
338 |
339 | @Override
340 | public DynamicWidget setHeight(final int height) {
341 | // It's dynamic, so ignore it.
342 | return this;
343 | }
344 |
345 | @Override
346 | public DynamicWidget setWidthHeight(final int width, final int height) {
347 | // It's dynamic, so ignore it.
348 | return this;
349 | }
350 |
351 | // Align
352 |
353 | public boolean isLeftAligned() {
354 | return leftAligned;
355 | }
356 |
357 | public Column setLeftAligned(final boolean leftAligned) {
358 | this.rightAligned = !leftAligned;
359 | this.leftAligned = leftAligned;
360 | return this;
361 | }
362 |
363 | public boolean isRightAligned() {
364 | return rightAligned;
365 | }
366 |
367 | public Column setRightAligned(final boolean rightAligned) {
368 | this.leftAligned = !rightAligned;
369 | this.rightAligned = rightAligned;
370 | return this;
371 | }
372 |
373 | public boolean isCenterAligned() {
374 | return this.leftAligned && this.rightAligned;
375 | }
376 |
377 | public Column setCenterAligned(final boolean centerAligned) {
378 | if (centerAligned) {
379 | this.leftAligned = true;
380 | this.rightAligned = true;
381 | return this;
382 | }
383 | // Default setting is left aligned.
384 | this.leftAligned = true;
385 | this.rightAligned = false;
386 | return this;
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/container/Row.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.container;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderFunction;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
6 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
8 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
9 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | @SuppressWarnings("unused")
16 | public class Row extends WidgetContainer {
17 | protected final List widgets = new ArrayList<>();
18 |
19 | protected boolean upAligned = true;
20 | protected boolean downAligned = false;
21 |
22 | @Override
23 | protected void preRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
24 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::preRender);
25 | }
26 |
27 | @Override
28 | protected void renderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
29 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::render);
30 | }
31 |
32 | @Override
33 | protected void postRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
34 | doRender(gui, renderSize, renderPos, mousePos, DynamicWidget::postRender);
35 | }
36 |
37 | protected void doRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos,
38 | final RenderFunction renderFunction) {
39 | int x = 0;
40 |
41 | int height = getHeight();
42 |
43 | for (final DynamicWidget widget : widgets) {
44 | if (widget.isDisabled()) {
45 | continue;
46 | }
47 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
48 | if (widgetRenderPos == null) {
49 | continue;
50 | }
51 | if (widget.isVisible()) {
52 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
53 | renderFunction.doRender(widget, gui, new RenderSize(widget.getWidth(), widget.getHeight()).smaller(renderSize), absRenderPos, mousePos.relativeTo(widgetRenderPos));
54 | }
55 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
56 | }
57 | }
58 |
59 | @Override
60 | public List getWidgets() {
61 | return widgets;
62 | }
63 |
64 | @Override
65 | public Row addWidget(final DynamicWidget widget) {
66 | widgets.add(widget);
67 | return this;
68 | }
69 |
70 | @Override
71 | public Row removeWidget(final DynamicWidget widget) {
72 | widgets.remove(widget);
73 | return this;
74 | }
75 |
76 | // GUI EventHandlers
77 |
78 | @Override
79 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
80 | int x = 0;
81 |
82 | int height = getHeight();
83 |
84 | for (final DynamicWidget widget : widgets) {
85 | if (widget.isDisabled()) {
86 | continue;
87 | }
88 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
89 | if (widgetRenderPos == null) {
90 | continue;
91 | }
92 | int offsetX = widgetRenderPos.posX();
93 | int offsetY = widgetRenderPos.posY();
94 |
95 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
96 | if (widget.isMouseOver(relativeMousePos)) {
97 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
98 | if (widget.onMouseClick(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton)) {
99 | return true;
100 | }
101 | }
102 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
103 | }
104 |
105 | return false;
106 | }
107 |
108 | @Override
109 | public void onMouseClickGlobal(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
110 | int x = 0;
111 |
112 | int height = getHeight();
113 |
114 | for (final DynamicWidget widget : widgets) {
115 | if (widget.isDisabled()) {
116 | continue;
117 | }
118 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
119 | if (widgetRenderPos == null) {
120 | continue;
121 | }
122 | int offsetX = widgetRenderPos.posX();
123 | int offsetY = widgetRenderPos.posY();
124 |
125 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
126 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
127 | widget.onMouseClickGlobal(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton);
128 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
129 | }
130 | }
131 |
132 | @Override
133 | public boolean onMouseClickMove(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
134 | int x = 0;
135 |
136 | int height = getHeight();
137 |
138 | for (final DynamicWidget widget : widgets) {
139 | if (widget.isDisabled()) {
140 | continue;
141 | }
142 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
143 | if (widgetRenderPos == null) {
144 | continue;
145 | }
146 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
147 | if (widget.onMouseClickMove(mousePos.relativeTo(widgetRenderPos), absRenderPos, mouseButton)) {
148 | return true;
149 | }
150 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
151 | }
152 | return false;
153 | }
154 |
155 | @Override
156 | public boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos) {
157 | int x = 0;
158 |
159 | int height = getHeight();
160 |
161 | for (final DynamicWidget widget : widgets) {
162 | if (widget.isDisabled()) {
163 | continue;
164 | }
165 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
166 | if (widgetRenderPos == null) {
167 | continue;
168 | }
169 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
170 | if (widget.onMouseReleased(mousePos.relativeTo(widgetRenderPos), absRenderPos)) {
171 | return true;
172 | }
173 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
174 | }
175 | return false;
176 | }
177 |
178 | @Override
179 | public boolean onMouseDWheel(final MousePos mousePos, final RenderPos renderPos, final int wheel) {
180 | int x = 0;
181 |
182 | int height = getHeight();
183 |
184 | for (final DynamicWidget widget : widgets) {
185 | if (widget.isDisabled()) {
186 | continue;
187 | }
188 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
189 | if (widgetRenderPos == null) {
190 | continue;
191 | }
192 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
193 | if (widget.onMouseDWheel(mousePos.relativeTo(widgetRenderPos), absRenderPos, wheel)) {
194 | return true;
195 | }
196 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
197 | }
198 | return false;
199 | }
200 |
201 | @Override
202 | public boolean onKeyTyped(final char typedChar, final int keyCode) {
203 | for (final DynamicWidget widget : widgets) {
204 | if (widget.isDisabled()) {
205 | continue;
206 | }
207 | if (widget.onKeyTyped(typedChar, keyCode)) {
208 | return true;
209 | }
210 | }
211 | return false;
212 | }
213 |
214 | // Tooltips
215 |
216 | @Override
217 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
218 | int x = 0;
219 |
220 | int height = getHeight();
221 |
222 | List tooltips = null;
223 |
224 | for (final DynamicWidget widget : widgets) {
225 | if (widget.isDisabled()) {
226 | continue;
227 | }
228 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
229 | if (widgetRenderPos == null) {
230 | continue;
231 | }
232 | int offsetX = widgetRenderPos.posX();
233 | int offsetY = widgetRenderPos.posY();
234 |
235 | MousePos relativeMousePos = mousePos.relativeTo(widgetRenderPos);
236 | if (widget.isMouseOver(relativeMousePos)) {
237 | List hoverTooltips = widget.getHoverTooltips(widgetGui, relativeMousePos);
238 | if (!hoverTooltips.isEmpty()) {
239 | tooltips = hoverTooltips;
240 | break;
241 | }
242 | }
243 |
244 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
245 | }
246 |
247 | return tooltips != null ? tooltips : Collections.emptyList();
248 | }
249 |
250 | // CustomEventHandlers
251 |
252 | @Override
253 | public boolean onGuiEvent(final GuiEvent event) {
254 | for (final DynamicWidget widget : widgets) {
255 | // if (widget.isDisabled()) {
256 | // continue;
257 | // }
258 | if (widget.onGuiEvent(event)) {
259 | return true;
260 | }
261 | }
262 | return false;
263 | }
264 |
265 | // Utils
266 |
267 | public RenderPos getWidgetRenderOffset(DynamicWidget widget, int height, int x) {
268 | int xOffset;
269 | int yOffset;
270 |
271 | if (widget.isUseAbsPos()) {
272 | xOffset = widget.getAbsX();
273 | yOffset = widget.getAbsY();
274 | } else if (isCenterAligned()) {
275 | xOffset = x + widget.getMarginLeft();
276 | yOffset = (height - (widget.getMarginUp() + widget.getHeight() + widget.getMarginDown())) / 2;
277 | } else if (upAligned) {
278 | xOffset = x + widget.getMarginLeft();
279 | yOffset = widget.getMarginUp();
280 | } else if (downAligned) {
281 | xOffset = x + widget.getMarginLeft();
282 | yOffset = height - (widget.getHeight() + widget.getMarginDown());
283 | } else {
284 | // Where does it align?
285 | return null;
286 | }
287 |
288 | return new RenderPos(xOffset, yOffset);
289 | }
290 |
291 | // X/Y Size
292 |
293 | @Override
294 | public int getWidth() {
295 | int width = 0;
296 | int absWidgetMaxWidth = 0;
297 | for (final DynamicWidget widget : widgets) {
298 | if (widget.isDisabled()) {
299 | continue;
300 | }
301 | int widgetWidth = widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
302 | if (widget.isUseAbsPos()) {
303 | absWidgetMaxWidth = Math.max(widgetWidth + widget.getAbsX(), absWidgetMaxWidth);
304 | continue;
305 | }
306 | width += widgetWidth;
307 | }
308 | return Math.max(width, absWidgetMaxWidth);
309 | }
310 |
311 | @Override
312 | public DynamicWidget setWidth(final int width) {
313 | // It's dynamic, so ignore it.
314 | return this;
315 | }
316 |
317 | @Override
318 | public int getHeight() {
319 | int maxY = 0;
320 | for (final DynamicWidget widget : widgets) {
321 | if (widget.isDisabled()) {
322 | continue;
323 | }
324 | int height = 0;
325 | if (widget.isUseAbsPos()) {
326 | height += widget.getAbsY();
327 | }
328 | height += widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
329 | if (height > maxY) {
330 | maxY = height;
331 | }
332 | }
333 | return maxY;
334 | }
335 |
336 | @Override
337 | public DynamicWidget setHeight(final int height) {
338 | // It's dynamic, so ignore it.
339 | return this;
340 | }
341 |
342 | @Override
343 | public DynamicWidget setWidthHeight(final int width, final int height) {
344 | // It's dynamic, so ignore it.
345 | return this;
346 | }
347 |
348 | // Align
349 |
350 | public boolean isUpAligned() {
351 | return upAligned;
352 | }
353 |
354 | public Row setUpAligned(final boolean upAligned) {
355 | this.downAligned = !upAligned;
356 | this.upAligned = upAligned;
357 | return this;
358 | }
359 |
360 | public boolean isDownAligned() {
361 | return downAligned;
362 | }
363 |
364 | public Row setDownAligned(final boolean downAligned) {
365 | this.upAligned = !downAligned;
366 | this.downAligned = downAligned;
367 | return this;
368 | }
369 |
370 | public Row setCenterAligned(final boolean centerAligned) {
371 | if (centerAligned) {
372 | this.upAligned = true;
373 | this.downAligned = true;
374 | return this;
375 | }
376 | // Default setting is up aligned.
377 | this.upAligned = true;
378 | this.downAligned = false;
379 | return this;
380 | }
381 |
382 | public boolean isCenterAligned() {
383 | return this.upAligned && this.downAligned;
384 | }
385 | }
386 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/container/WidgetContainer.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.container;
2 |
3 | import github.kasuminova.ae2ctl.AE2CTLegacy;
4 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
6 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
8 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
9 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
10 | import net.minecraft.client.gui.ScaledResolution;
11 | import org.lwjgl.opengl.GL11;
12 |
13 | import java.awt.*;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 |
17 | /**
18 | * 一个轻量化的组件库核心类,用于存储一系列 {@link DynamicWidget} 并使用它自己的方式来渲染这些组件。
19 | *
20 | *
21 | * A lightweight component library core class, used to store a series of {@link DynamicWidget} and render them using its own way.
22 | */
23 | public abstract class WidgetContainer extends DynamicWidget {
24 | protected static final ThreadLocal> SCISSOR_STACK = ThreadLocal.withInitial(LinkedList::new);
25 |
26 | protected boolean useScissor = true;
27 |
28 | public static void pushScissor(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final int width, final int height) {
29 | final int guiLeft = gui.getGuiLeft();
30 | final int guiTop = gui.getGuiTop();
31 |
32 | int offsetX = renderPos.posX();
33 | int offsetY = renderPos.posY();
34 |
35 | if (renderSize.isLimited()) {
36 | LinkedList scissorStack = SCISSOR_STACK.get();
37 |
38 | ScaledResolution res = new ScaledResolution(gui.getGui().mc);
39 | int scissorWidth = renderSize.isWidthLimited() ? renderSize.width() : width;
40 | int scissorHeight = renderSize.isHeightLimited() ? renderSize.height() : height;
41 |
42 | Rectangle scissorFrame = new Rectangle(
43 | (guiLeft + offsetX) * res.getScaleFactor(),
44 | // y is left bottom...
45 | gui.getGui().mc.displayHeight - ((guiTop + offsetY + scissorHeight) * res.getScaleFactor()),
46 | scissorWidth * res.getScaleFactor(),
47 | scissorHeight * res.getScaleFactor()
48 | );
49 |
50 | if (scissorStack.peekFirst() == null) {
51 | GL11.glEnable(GL11.GL_SCISSOR_TEST);
52 | }
53 | GL11.glScissor(scissorFrame.x, scissorFrame.y, scissorFrame.width, scissorFrame.height);
54 |
55 | scissorStack.push(scissorFrame);
56 | }
57 | }
58 |
59 | public static void popScissor(final RenderSize renderSize) {
60 | if (renderSize.isLimited()) {
61 | LinkedList scissorStack = SCISSOR_STACK.get();
62 | if (scissorStack.peekFirst() != null) {
63 | scissorStack.pop();
64 | }
65 |
66 | Rectangle prevScissorFrame = scissorStack.peekFirst();
67 | if (prevScissorFrame == null) {
68 | GL11.glDisable(GL11.GL_SCISSOR_TEST);
69 | } else {
70 | GL11.glScissor(prevScissorFrame.x, prevScissorFrame.y, prevScissorFrame.width, prevScissorFrame.height);
71 | }
72 | }
73 | }
74 |
75 | public static void enableScissor() {
76 | LinkedList scissorStack = SCISSOR_STACK.get();
77 | Rectangle scissorFrame = scissorStack.peekFirst();
78 | if (scissorFrame != null) {
79 | GL11.glEnable(GL11.GL_SCISSOR_TEST);
80 | GL11.glScissor(scissorFrame.x, scissorFrame.y, scissorFrame.width, scissorFrame.height);
81 | }
82 | }
83 |
84 | public static void disableScissor() {
85 | GL11.glDisable(GL11.GL_SCISSOR_TEST);
86 | }
87 |
88 | @Override
89 | public final void preRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
90 | boolean useScissor = this.useScissor;
91 | if (useScissor) {
92 | pushScissor(gui, renderSize, renderPos, getWidth(), getHeight());
93 | }
94 | try {
95 | preRenderInternal(gui, renderSize, renderPos, mousePos);
96 | } catch (Exception e) {
97 | SCISSOR_STACK.get().clear();
98 | AE2CTLegacy.log.error("Error when rendering dynamic widgets!", e);
99 | throw e;
100 | } finally {
101 | if (useScissor) {
102 | popScissor(renderSize);
103 | }
104 | }
105 | }
106 |
107 | @Override
108 | public final void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
109 | boolean useScissor = this.useScissor;
110 | if (useScissor) {
111 | pushScissor(gui, renderSize, renderPos, getWidth(), getHeight());
112 | }
113 | try {
114 | renderInternal(gui, renderSize, renderPos, mousePos);
115 | } catch (Exception e) {
116 | SCISSOR_STACK.get().clear();
117 | AE2CTLegacy.log.error("Error when rendering dynamic widgets!", e);
118 | throw e;
119 | } finally {
120 | if (useScissor) {
121 | popScissor(renderSize);
122 | }
123 | }
124 | }
125 |
126 | @Override
127 | public final void postRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
128 | boolean useScissor = this.useScissor;
129 | if (useScissor) {
130 | pushScissor(gui, renderSize, renderPos, getWidth(), getHeight());
131 | }
132 | try {
133 | postRenderInternal(gui, renderSize, renderPos, mousePos);
134 | } catch (Exception e) {
135 | SCISSOR_STACK.get().clear();
136 | AE2CTLegacy.log.error("Error when rendering dynamic widgets!", e);
137 | throw e;
138 | } finally {
139 | if (useScissor) {
140 | popScissor(renderSize);
141 | }
142 | }
143 | }
144 |
145 | protected abstract void preRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos);
146 |
147 | protected abstract void renderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos);
148 |
149 | protected abstract void postRenderInternal(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos);
150 |
151 | public abstract List getWidgets();
152 |
153 | public abstract WidgetContainer addWidget(DynamicWidget widget);
154 |
155 | public WidgetContainer addWidgets(DynamicWidget... widgets) {
156 | for (final DynamicWidget widget : widgets) {
157 | addWidget(widget);
158 | }
159 | return this;
160 | }
161 |
162 | public abstract WidgetContainer removeWidget(DynamicWidget widget);
163 |
164 | // GUI EventHandlers
165 |
166 | @Override
167 | public void update(final WidgetGui gui) {
168 | for (DynamicWidget widget : getWidgets()) {
169 | if (widget.isDisabled()) {
170 | continue;
171 | }
172 | widget.update(gui);
173 | }
174 | }
175 |
176 | @Override
177 | public void onGUIClosed(final WidgetGui gui) {
178 | getWidgets().forEach(widget -> widget.onGUIClosed(gui));
179 | }
180 |
181 | @Override
182 | public void initWidget(final WidgetGui gui) {
183 | getWidgets().forEach(widget -> widget.initWidget(gui));
184 | }
185 |
186 | @Override
187 | public abstract boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton);
188 |
189 | @Override
190 | public abstract void onMouseClickGlobal(final MousePos mousePos, final RenderPos renderPos, final int mouseButton);
191 |
192 | @Override
193 | public abstract boolean onMouseReleased(final MousePos mousePos, final RenderPos renderPos);
194 |
195 | @Override
196 | public abstract boolean onMouseDWheel(final MousePos mousePos, final RenderPos renderPos, final int wheel);
197 |
198 | @Override
199 | public abstract boolean onKeyTyped(final char typedChar, final int keyCode);
200 |
201 | // Tooltips
202 |
203 | @Override
204 | public abstract List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos);
205 |
206 | // Events
207 |
208 | /**
209 | * GUI events will only be passed down, not up.
210 | *
211 | * @return Returns true to terminate the passing of events to the widgets.
212 | */
213 | @Override
214 | public abstract boolean onGuiEvent(final GuiEvent event);
215 |
216 | // Scissor setting
217 |
218 | public boolean isUseScissor() {
219 | return useScissor;
220 | }
221 |
222 | public WidgetContainer setUseScissor(final boolean useScissor) {
223 | this.useScissor = useScissor;
224 | return this;
225 | }
226 | }
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/event/GuiEvent.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.event;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
4 |
5 | public abstract class GuiEvent {
6 | protected final WidgetGui gui;
7 |
8 | public GuiEvent(final WidgetGui gui) {
9 | this.gui = gui;
10 | }
11 |
12 | public WidgetGui getGui() {
13 | return gui;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/event/WidgetEvent.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.event;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
4 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
5 |
6 | public abstract class WidgetEvent extends GuiEvent {
7 | protected final DynamicWidget sender;
8 |
9 | public WidgetEvent(final WidgetGui gui, final DynamicWidget sender) {
10 | super(gui);
11 | this.sender = sender;
12 | }
13 |
14 | public DynamicWidget getSender() {
15 | return sender;
16 | }
17 | }
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/Background.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree;
2 |
3 | import github.kasuminova.ae2ctl.AE2CTLegacy;
4 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
5 | import net.minecraft.util.ResourceLocation;
6 |
7 | public enum Background {
8 |
9 | BG_256_256_LIGHT(
10 | TextureProperties.of(
11 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_256x256_light.png"),
12 | 0, 0, 256, 256
13 | ), false
14 | ),
15 | BG_256_256_DARK(
16 | TextureProperties.of(
17 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_256x256_dark.png"),
18 | 0, 0, 256, 256
19 | ), true
20 | ),
21 | BG_320_256_LIGHT(
22 | TextureProperties.of(
23 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_320x256_light.png"),
24 | 0, 0, 320, 256
25 | ), false
26 | ),
27 | BG_320_256_DARK(
28 | TextureProperties.of(
29 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_320x256_dark.png"),
30 | 0, 0, 320, 256
31 | ), true
32 | ),
33 | BG_384_320_LIGHT(
34 | TextureProperties.of(
35 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_384x320_light.png"),
36 | 0, 0, 384, 320
37 | ), false
38 | ),
39 | BG_384_320_DARK(
40 | TextureProperties.of(
41 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_384x320_dark.png"),
42 | 0, 0, 384, 320
43 | ), true
44 | ),
45 | BG_512_320_LIGHT(
46 | TextureProperties.of(
47 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_512x320_light.png"),
48 | 0, 0, 512, 320
49 | ), false
50 | ),
51 | BG_512_320_DARK(
52 | TextureProperties.of(
53 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_512x320_dark.png"),
54 | 0, 0, 512, 320
55 | ), true
56 | ),
57 | BG_640_384_LIGHT(
58 | TextureProperties.of(
59 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_640x384_light.png"),
60 | 0, 0, 640, 384
61 | ), false
62 | ),
63 | BG_640_384_DARK(
64 | TextureProperties.of(
65 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_640x384_dark.png"),
66 | 0, 0, 640, 384
67 | ), true
68 | ),
69 | ;
70 |
71 | private final TextureProperties texture;
72 | private final int internalWidth;
73 | private final int internalHeight;
74 | private final int internalXOffset;
75 | private final int internalYOffset;
76 | private final boolean dark;
77 |
78 | Background(final TextureProperties texture, final boolean dark) {
79 | this.texture = texture;
80 | this.dark = dark;
81 | this.internalWidth = texture.width() - (7 * 2);
82 | this.internalHeight = texture.height() - (25 + 9);
83 | this.internalXOffset = 7;
84 | this.internalYOffset = 25;
85 | }
86 |
87 | public TextureProperties getTexture() {
88 | return texture;
89 | }
90 |
91 | public int getInternalWidth() {
92 | return internalWidth;
93 | }
94 |
95 | public int getInternalHeight() {
96 | return internalHeight;
97 | }
98 |
99 | public int getInternalXOffset() {
100 | return internalXOffset;
101 | }
102 |
103 | public int getInternalYOffset() {
104 | return internalYOffset;
105 | }
106 |
107 | public static Background getLargest(final int screenWidth, final int screenHeight, final boolean dark) {
108 | for (int i = values().length - 1; i >= 0; i--) {
109 | Background bg = values()[i];
110 | if (bg.dark != dark) {
111 | continue;
112 | }
113 | if (screenWidth >= bg.texture.width() * 1.25 && screenHeight >= bg.texture.height()) {
114 | return bg;
115 | }
116 | }
117 | return dark ? BG_256_256_DARK : BG_256_256_LIGHT;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/PlaceHolder.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
6 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
8 |
9 | public class PlaceHolder extends DynamicWidget {
10 |
11 | public PlaceHolder() {
12 | setVisible(false);
13 | }
14 |
15 | @Override
16 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
17 | // Do nothing
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/TreeNode.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree;
2 |
3 | import appeng.api.storage.data.IAEItemStack;
4 | import appeng.client.render.StackSizeRenderer;
5 | import github.kasuminova.ae2ctl.AE2CTLegacy;
6 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
7 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
8 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
9 | import github.kasuminova.ae2ctl.client.gui.util.TextureProperties;
10 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
11 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
12 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
13 | import github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.event.TreeNodeSelectEvent;
14 | import github.kasuminova.ae2ctl.client.handler.ClientTickHandler;
15 | import github.kasuminova.ae2ctl.common.integration.JEIUtils;
16 | import github.kasuminova.ae2ctl.common.integration.ae2.data.LiteCraftTreeNode;
17 | import github.kasuminova.ae2ctl.common.integration.ae2.data.LiteCraftTreeProc;
18 | import github.kasuminova.ae2ctl.common.util.NumberUtils;
19 | import mezz.jei.api.IJeiRuntime;
20 | import mezz.jei.api.recipe.IFocus;
21 | import mezz.jei.config.KeyBindings;
22 | import net.minecraft.client.Minecraft;
23 | import net.minecraft.client.gui.FontRenderer;
24 | import net.minecraft.client.gui.GuiScreen;
25 | import net.minecraft.client.gui.inventory.GuiContainer;
26 | import net.minecraft.client.renderer.GlStateManager;
27 | import net.minecraft.client.renderer.RenderHelper;
28 | import net.minecraft.client.resources.I18n;
29 | import net.minecraft.item.ItemStack;
30 | import net.minecraft.util.ResourceLocation;
31 | import net.minecraftforge.fml.client.config.GuiUtils;
32 |
33 | import java.util.Collections;
34 | import java.util.List;
35 |
36 | public class TreeNode extends DynamicWidget {
37 |
38 | public static final int LINE_WIDTH = 1;
39 | public static final int LINE_HEIGHT = 2;
40 |
41 | public static final int PARENT_LINE_HEIGHT = 2;
42 | public static final int LINE_TOTAL_HEIGHT = 6;
43 |
44 | public static final int WIDTH = 20;
45 | public static final int HEIGHT = 20;
46 | public static final int TOTAL_HEIGHT = HEIGHT + LINE_TOTAL_HEIGHT;
47 |
48 | public static final int ROOT_MARGIN_TOP = 4;
49 | public static final int MARGIN_LEFT = 6;
50 | public static final int LINE_RENDER_OFFSET = (WIDTH - (LINE_WIDTH * 2)) / 2;
51 |
52 | public static final int LINE_COLOR = 0xFFF2F2F2;
53 | public static final int LINE_SHADOW_COLOR = 0xFF4D4D67;
54 |
55 | public static final int MISSING_LINE_COLOR = 0xFFEE6363;
56 | public static final int MISSING_LINE_SHADOW_COLOR = 0xFF8B3A3A;
57 |
58 | public static final int ITEM_RENDER_OFFSET = 2;
59 |
60 | private static final TextureProperties BACKGROUND = TextureProperties.of(
61 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
62 | 0, 216, 20, 20
63 | );
64 |
65 | private static final TextureProperties BACKGROUND_MISSING = TextureProperties.of(
66 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
67 | 0, 236, 20, 20
68 | );
69 |
70 | private static final TextureProperties MISSING = TextureProperties.of(
71 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
72 | 40, 216, 20, 20
73 | );
74 |
75 | private static final TextureProperties SELECTED = TextureProperties.of(
76 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_dark.png"),
77 | 0, 196, 20, 20
78 | );
79 |
80 | private static final StackSizeRenderer STACK_SIZE_RENDERER = new StackSizeRenderer();
81 |
82 | protected final CraftingTree tree;
83 |
84 | protected boolean root = false;
85 | protected int linkedSubNodes = 0;
86 |
87 | protected LiteCraftTreeNode node = null;
88 | protected TreeRow parentRow = null;
89 | protected TreeNode prev = null;
90 | protected TreeNode next = null;
91 |
92 | protected boolean mouseOver = false;
93 | protected boolean selected = false;
94 |
95 | public TreeNode(final CraftingTree tree) {
96 | this.tree = tree;
97 | setHeight(TOTAL_HEIGHT);
98 | setMargin(MARGIN_LEFT, 0, 0, 0);
99 | }
100 |
101 | @Override
102 | public void update(final WidgetGui gui) {
103 | super.update(gui);
104 | mouseOver = false;
105 | }
106 |
107 | @Override
108 | public void preRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
109 | mouseOver = isMouseOver(mousePos.mouseX(), mousePos.mouseY());
110 | if (renderPos.posX() + WIDTH < 0 && renderPos.posY() + TOTAL_HEIGHT < 0) {
111 | return;
112 | }
113 |
114 | GlStateManager.enableBlend();
115 | GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
116 | if (LiteCraftTreeNode.isMissing(node)) {
117 | BACKGROUND_MISSING.render(renderPos.add(new RenderPos(0, LINE_HEIGHT)), gui);
118 | } else {
119 | BACKGROUND.render(renderPos.add(new RenderPos(0, LINE_HEIGHT)), gui);
120 | }
121 | if (selected) {
122 | SELECTED.render(renderPos.add(new RenderPos(0, LINE_HEIGHT)), gui);
123 | }
124 | }
125 |
126 | @Override
127 | public void render(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
128 | if (renderPos.posX() + WIDTH < 0 && renderPos.posY() + TOTAL_HEIGHT < 0) {
129 | return;
130 | }
131 | renderItem(renderPos.add(new RenderPos(0, LINE_HEIGHT)).add(new RenderPos(ITEM_RENDER_OFFSET, ITEM_RENDER_OFFSET)));
132 | if (LiteCraftTreeNode.isMissing(node) && node.missing() > 0) {
133 | GlStateManager.disableDepth();
134 | MISSING.render(renderPos.add(new RenderPos(0, LINE_HEIGHT)), gui);
135 | GlStateManager.enableDepth();
136 | }
137 | GlStateManager.disableLighting();
138 | }
139 |
140 | @Override
141 | public void postRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos) {
142 | renderParentNodeLink(renderPos);
143 | renderSubNodeLinkLine(renderPos.add(new RenderPos(0, HEIGHT + LINE_HEIGHT)));
144 | CraftingTree.DEBUG_RENDERED_NODES.incrementAndGet();
145 | }
146 |
147 | @Override
148 | public List getHoverTooltips(final WidgetGui widgetGui, final MousePos mousePos) {
149 | IAEItemStack output = node.output();
150 | if (output == null) {
151 | return Collections.emptyList();
152 | }
153 |
154 | ItemStack definition = output.getDefinition();
155 | GuiScreen g = widgetGui.getGui();
156 | GuiUtils.preItemToolTip(definition);
157 |
158 | List toolTip = g.getItemToolTip(definition);
159 | if (LiteCraftTreeNode.isMissing(node)) {
160 | toolTip.add(node.missing() > 0
161 | ? I18n.format("gui.crafting_tree.missing", NumberUtils.formatDecimal((node.missing())))
162 | : I18n.format("gui.crafting_tree.sub_node_missing"));
163 | }
164 | if (output.getStackSize() >= 10000) {
165 | toolTip.add(NumberUtils.formatDecimal(output.getStackSize()));
166 | }
167 |
168 | return toolTip;
169 | }
170 |
171 | protected void renderItem(final RenderPos renderPos) {
172 | if (node.output() == null) {
173 | return;
174 | }
175 |
176 | Minecraft minecraft = Minecraft.getMinecraft();
177 | FontRenderer fr = minecraft.fontRenderer;
178 | ItemStack stack = node.output().getCachedItemStack(1);
179 | {
180 | GlStateManager.color(1f, 1f, 1f, 1f);
181 | GlStateManager.enableDepth();
182 | RenderHelper.enableGUIStandardItemLighting();
183 | minecraft.getRenderItem().renderItemAndEffectIntoGUI(null, stack, renderPos.posX(), renderPos.posY());
184 | minecraft.getRenderItem().renderItemOverlayIntoGUI(fr, stack, renderPos.posX(), renderPos.posY(), "");
185 | }
186 | STACK_SIZE_RENDERER.renderStackSize(fr, node.output(), renderPos.posX(), renderPos.posY());
187 | RenderHelper.disableStandardItemLighting();
188 | }
189 |
190 | protected void renderParentNodeLink(final RenderPos renderPos) {
191 | if (root) {
192 | return;
193 | }
194 |
195 | int lineColor = LiteCraftTreeNode.isMissing(node) ? MISSING_LINE_COLOR : LINE_COLOR;
196 | int lineShadowColor = LiteCraftTreeNode.isMissing(node) ? MISSING_LINE_SHADOW_COLOR : LINE_SHADOW_COLOR;
197 |
198 | // Vertical Line
199 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET, -1)), LINE_WIDTH, LINE_HEIGHT + 1, lineColor);
200 | // Shadow
201 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET + 1, 0)), LINE_WIDTH, LINE_HEIGHT, lineShadowColor);
202 | }
203 |
204 | protected void renderSubNodeLinkLine(final RenderPos renderPos) {
205 | if (node.inputs().isEmpty()) {
206 | return;
207 | }
208 |
209 | int lineColor = LiteCraftTreeNode.isMissing(node) ? MISSING_LINE_COLOR : LINE_COLOR;
210 | int lineShadowColor = LiteCraftTreeNode.isMissing(node) ? MISSING_LINE_SHADOW_COLOR : LINE_SHADOW_COLOR;
211 |
212 | if (linkedSubNodes > 0) {
213 | int totalWidth = (linkedSubNodes * (WIDTH + MARGIN_LEFT)) + 1;
214 | // Horizontal Line
215 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET, LINE_HEIGHT)), totalWidth, 1, lineColor);
216 | // Shadow
217 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET + 1, LINE_HEIGHT + 1)), totalWidth, 1, lineShadowColor);
218 |
219 | // Vertical Line
220 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET, 0)), LINE_WIDTH, LINE_HEIGHT, lineColor);
221 | // Shadow
222 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET + 1, 0)), LINE_WIDTH, LINE_HEIGHT, lineShadowColor);
223 | } else {
224 | // Vertical Line
225 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET, 0)), LINE_WIDTH, LINE_HEIGHT + 1, lineColor);
226 | // Shadow
227 | renderLine(renderPos.add(new RenderPos(LINE_RENDER_OFFSET + 1, 0)), LINE_WIDTH, LINE_HEIGHT + 2, lineShadowColor);
228 | }
229 | }
230 |
231 | protected static void renderLine(final RenderPos pos, final int width, final int height, final int color) {
232 | GuiContainer.drawRect(pos.posX(), pos.posY(), pos.posX() + width, pos.posY() + height, color);
233 | }
234 |
235 | @Override
236 | public boolean onGuiEvent(final GuiEvent event) {
237 | if (event instanceof TreeNodeSelectEvent selectEvent) {
238 | if (selected && selectEvent.getSelectedNode() != this) {
239 | selected = false;
240 | }
241 | }
242 | return super.onGuiEvent(event);
243 | }
244 |
245 | @Override
246 | public boolean onMouseClick(final MousePos mousePos, final RenderPos renderPos, final int mouseButton) {
247 | if (!selected) {
248 | select();
249 | return true;
250 | }
251 | return false;
252 | }
253 |
254 | @Override
255 | public boolean onKeyTyped(final char typedChar, final int keyCode) {
256 | if (!mouseOver || node == null) {
257 | return false;
258 | }
259 |
260 | int showRecipeKeyCode = KeyBindings.showRecipe.getKeyCode();
261 | int showUsesKeyCode = KeyBindings.showUses.getKeyCode();
262 | int bookmarkKeyCode = KeyBindings.bookmark.getKeyCode();
263 |
264 | if (showRecipeKeyCode > 0 && showRecipeKeyCode <= 255 && showRecipeKeyCode == keyCode) {
265 | return showStackFocus(IFocus.Mode.OUTPUT);
266 | }
267 | if (showUsesKeyCode > 0 && showUsesKeyCode <= 255 && showUsesKeyCode == keyCode) {
268 | return showStackFocus(IFocus.Mode.INPUT);
269 | }
270 | if (bookmarkKeyCode > 0 && bookmarkKeyCode <= 255 && bookmarkKeyCode == keyCode) {
271 | JEIUtils.addItemStackToBookmarkList(node.output().getCachedItemStack(1));
272 | return true;
273 | }
274 |
275 | return false;
276 | }
277 |
278 | protected boolean showStackFocus(final IFocus.Mode output) {
279 | ClientTickHandler.addTask(() -> {
280 | IJeiRuntime runtime = JEIUtils.getJeiRuntime();
281 | IFocus focus = runtime.getRecipeRegistry().createFocus(output, node.output().getCachedItemStack(1));
282 | runtime.getRecipesGui().show(focus);
283 | });
284 | return true;
285 | }
286 |
287 | protected int getTotalSubNodes() {
288 | int total = 0;
289 | for (final LiteCraftTreeProc input : node.inputs()) {
290 | total += input.inputs().size();
291 | }
292 | return total;
293 | }
294 |
295 | public TreeNode select() {
296 | selected = true;
297 | tree.onGuiEvent(new TreeNodeSelectEvent(this));
298 | return this;
299 | }
300 |
301 | public TreeNode setNode(final LiteCraftTreeNode node) {
302 | this.node = node;
303 | return this;
304 | }
305 |
306 | public TreeRow getParentRow() {
307 | return parentRow;
308 | }
309 |
310 | public TreeNode setParentRow(final TreeRow parentRow) {
311 | this.parentRow = parentRow;
312 | return this;
313 | }
314 |
315 | public TreeNode getPrev() {
316 | return prev;
317 | }
318 |
319 | public TreeNode getNext() {
320 | return next;
321 | }
322 |
323 | public TreeNode setPrev(final TreeNode prev) {
324 | this.prev = prev;
325 | return this;
326 | }
327 |
328 | public TreeNode setNext(final TreeNode next) {
329 | this.next = next;
330 | return this;
331 | }
332 |
333 | public TreeNode setLinkedSubNodes(final int linkedSubNodes) {
334 | this.linkedSubNodes = linkedSubNodes;
335 | return this;
336 | }
337 |
338 | public TreeNode setRoot(final boolean root) {
339 | this.root = root;
340 | return this;
341 | }
342 |
343 | @Override
344 | public boolean isMouseOver(final MousePos mousePos) {
345 | return isMouseOver(0, PARENT_LINE_HEIGHT, mousePos.mouseX(), mousePos.mouseY(), WIDTH, HEIGHT);
346 | }
347 |
348 | public boolean isMouseOver(int startX, int startY, int mouseX, int mouseY, int width, int height) {
349 | if (isInvisible()) {
350 | return false;
351 | }
352 |
353 | int endX = startX + width;
354 | int endY = startY + height;
355 | return mouseX >= startX && mouseX < endX && mouseY >= startY && mouseY < endY;
356 | }
357 |
358 | }
359 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/TreeRow.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.util.MousePos;
4 | import github.kasuminova.ae2ctl.client.gui.util.RenderFunction;
5 | import github.kasuminova.ae2ctl.client.gui.util.RenderPos;
6 | import github.kasuminova.ae2ctl.client.gui.util.RenderSize;
7 | import github.kasuminova.ae2ctl.client.gui.widget.base.DynamicWidget;
8 | import github.kasuminova.ae2ctl.client.gui.widget.base.WidgetGui;
9 | import github.kasuminova.ae2ctl.client.gui.widget.container.Row;
10 |
11 | public class TreeRow extends Row {
12 |
13 | private int cachedWidth = -1;
14 | private int cachedHeight = -1;
15 | private int cachedWidgetSize = -1;
16 |
17 | public TreeRow() {
18 | setUseScissor(false);
19 | }
20 |
21 | @Override
22 | protected void doRender(final WidgetGui gui, final RenderSize renderSize, final RenderPos renderPos, final MousePos mousePos, final RenderFunction renderFunction) {
23 | int x = 0;
24 |
25 | int height = getHeight();
26 |
27 | for (int i = 0; i < widgets.size(); i++) {
28 | final DynamicWidget widget = widgets.get(i);
29 | if (widget.isDisabled()) {
30 | continue;
31 | }
32 | if (widget instanceof PlaceHolder) {
33 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
34 | continue;
35 | }
36 |
37 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
38 | if (widgetRenderPos == null) {
39 | continue;
40 | }
41 |
42 | if (widget.isVisible()) {
43 | RenderPos absRenderPos = widgetRenderPos.add(renderPos);
44 | int totalWidth = widget.getWidth() + widget.getMarginRight() + getPlaceHolderWidth(i + 1);
45 | if (absRenderPos.posX() + totalWidth > 0) {
46 | renderFunction.doRender(widget, gui, new RenderSize(widget.getWidth(), widget.getHeight()).smaller(renderSize), absRenderPos, mousePos.relativeTo(absRenderPos));
47 | }
48 | }
49 |
50 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
51 | if (renderSize.isWidthLimited() && x > renderSize.width()) {
52 | break;
53 | }
54 | }
55 | }
56 |
57 | public int getPlaceHolderWidth(final int startIdx) {
58 | int width = 0;
59 | for (int i = startIdx; i < widgets.size(); i++) {
60 | DynamicWidget widget = widgets.get(i);
61 | if (widget instanceof PlaceHolder) {
62 | width += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
63 | } else {
64 | break;
65 | }
66 | }
67 | return width;
68 | }
69 |
70 | public RenderPos getRelativeRenderPos(final TreeNode node) {
71 | if (!widgets.contains(node)) {
72 | return null;
73 | }
74 |
75 | int x = 0;
76 | int height = getHeight();
77 |
78 | for (final DynamicWidget widget : widgets) {
79 | if (widget.isDisabled()) {
80 | continue;
81 | }
82 |
83 | RenderPos widgetRenderPos = getWidgetRenderOffset(widget, height, x);
84 | if (widgetRenderPos == null) {
85 | continue;
86 | }
87 | if (widget == node) {
88 | return widgetRenderPos;
89 | }
90 |
91 | x += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
92 | }
93 |
94 | return null;
95 | }
96 |
97 | @Override
98 | public TreeRow addWidget(final DynamicWidget widget) {
99 | super.addWidget(widget);
100 | if (cachedWidgetSize + 1 == widgets.size()) {
101 | cachedWidgetSize++;
102 | cachedWidth += widget.getMarginLeft() + widget.getWidth() + widget.getMarginRight();
103 |
104 | int widgetHeight = widget.getMarginUp() + widget.getHeight() + widget.getMarginDown();
105 | if (widget.isUseAbsPos()) {
106 | widgetHeight += widget.getAbsY();
107 | }
108 | if (widgetHeight > cachedHeight) {
109 | cachedHeight = widgetHeight;
110 | }
111 | } else {
112 | cachedWidgetSize = widgets.size();
113 | cachedWidth = super.getWidth();
114 | cachedHeight = super.getHeight();
115 | }
116 | return this;
117 | }
118 |
119 | @Override
120 | public int getWidth() {
121 | if (cachedWidgetSize == widgets.size()) {
122 | return cachedWidth;
123 | }
124 | cachedWidgetSize = widgets.size();
125 | cachedHeight = super.getHeight();
126 | cachedWidth = super.getWidth();
127 | return cachedWidth;
128 | }
129 |
130 | @Override
131 | public int getHeight() {
132 | if (cachedWidgetSize == widgets.size()) {
133 | return cachedHeight;
134 | }
135 | cachedWidgetSize = widgets.size();
136 | cachedWidth = super.getWidth();
137 | cachedHeight = super.getHeight();
138 | return cachedHeight;
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/event/CraftingTreeDataUpdateEvent.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.event;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
4 | import github.kasuminova.ae2ctl.common.integration.ae2.data.LiteCraftTreeNode;
5 |
6 | public class CraftingTreeDataUpdateEvent extends GuiEvent {
7 |
8 | private final LiteCraftTreeNode root;
9 |
10 | public CraftingTreeDataUpdateEvent(final LiteCraftTreeNode root) {
11 | super(null);
12 | this.root = root;
13 | }
14 |
15 | public LiteCraftTreeNode getRoot() {
16 | return root;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/impl/craftingtree/event/TreeNodeSelectEvent.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.event;
2 |
3 | import github.kasuminova.ae2ctl.client.gui.widget.event.GuiEvent;
4 | import github.kasuminova.ae2ctl.client.gui.widget.impl.craftingtree.TreeNode;
5 |
6 | public class TreeNodeSelectEvent extends GuiEvent {
7 |
8 | private final TreeNode selectedNode;
9 |
10 | public TreeNodeSelectEvent(final TreeNode selectedNode) {
11 | super(null);
12 | this.selectedNode = selectedNode;
13 | }
14 |
15 | public TreeNode getSelectedNode() {
16 | return selectedNode;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/gui/widget/vanilla/GuiButtonImageExt.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.gui.widget.vanilla;
2 |
3 | import appeng.client.gui.widgets.ITooltip;
4 | import net.minecraft.client.Minecraft;
5 | import net.minecraft.client.gui.GuiButtonImage;
6 | import net.minecraft.client.renderer.GlStateManager;
7 | import net.minecraft.util.ResourceLocation;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class GuiButtonImageExt extends GuiButtonImage implements ITooltip {
11 |
12 | private String message = "";
13 |
14 | public GuiButtonImageExt(final int buttonId, final int xIn, final int yIn, final int widthIn, final int heightIn, final int textureOffestX, final int textureOffestY, final int p_i47392_8_, final ResourceLocation resource) {
15 | super(buttonId, xIn, yIn, widthIn, heightIn, textureOffestX, textureOffestY, p_i47392_8_, resource);
16 | }
17 |
18 | @Override
19 | public void drawButton(final @NotNull Minecraft mc, final int mouseX, final int mouseY, final float partialTicks) {
20 | GlStateManager.disableLighting();
21 | GlStateManager.enableBlend();
22 | GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
23 | GlStateManager.color(1f, 1f, 1f, 1f);
24 | super.drawButton(mc, mouseX, mouseY, partialTicks);
25 | }
26 |
27 | public GuiButtonImageExt setMessage(final String message) {
28 | this.message = message;
29 | return this;
30 | }
31 |
32 | @Override
33 | public String getMessage() {
34 | return message;
35 | }
36 |
37 | @Override
38 | public int xPos() {
39 | return x;
40 | }
41 |
42 | @Override
43 | public int yPos() {
44 | return y;
45 | }
46 |
47 | @Override
48 | public int getWidth() {
49 | return width;
50 | }
51 |
52 | @Override
53 | public int getHeight() {
54 | return height;
55 | }
56 |
57 | @Override
58 | public boolean isVisible() {
59 | return visible;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/client/handler/ClientTickHandler.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.client.handler;
2 |
3 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
4 | import net.minecraftforge.fml.common.gameevent.TickEvent;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class ClientTickHandler {
10 |
11 | private static final List TASKS = new ArrayList<>();
12 |
13 | @SubscribeEvent
14 | public static void onClientTickEnd(final TickEvent.ClientTickEvent event) {
15 | if (event.phase != TickEvent.Phase.END) {
16 | return;
17 | }
18 | TASKS.forEach(Runnable::run);
19 | TASKS.clear();
20 | }
21 |
22 | public static void addTask(final Runnable task) {
23 | TASKS.add(task);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/CommonProxy.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common;
2 |
3 | import appeng.api.storage.ITerminalHost;
4 | import appeng.container.implementations.ContainerCraftConfirm;
5 | import github.kasuminova.ae2ctl.AE2CTLegacy;
6 | import github.kasuminova.ae2ctl.client.ClientProxy;
7 | import github.kasuminova.ae2ctl.common.container.ContainerCraftingTree;
8 | import net.minecraft.entity.player.EntityPlayer;
9 | import net.minecraft.util.math.MathHelper;
10 | import net.minecraft.world.World;
11 | import net.minecraftforge.fml.common.network.IGuiHandler;
12 | import net.minecraftforge.fml.common.network.NetworkRegistry;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | public class CommonProxy implements IGuiHandler {
16 |
17 | public CommonProxy() {
18 | }
19 |
20 | public void construction() {
21 |
22 | }
23 |
24 | public void preInit() {
25 | NetworkRegistry.INSTANCE.registerGuiHandler(AE2CTLegacy.MOD_ID, this);
26 | }
27 |
28 | public void init() {
29 |
30 | }
31 |
32 | public void postInit() {
33 |
34 | }
35 |
36 | public void loadComplete() {
37 |
38 | }
39 |
40 | @Nullable
41 | @Override
42 | public Object getServerGuiElement(final int ID, final EntityPlayer player, final World world, final int x, final int y, final int z) {
43 | ClientProxy.GuiType type = ClientProxy.GuiType.values()[MathHelper.clamp(ID, 0, ClientProxy.GuiType.values().length - 1)];
44 | return switch (type) {
45 | case CRAFTING_TREE -> {
46 | if (!(player.openContainer instanceof ContainerCraftConfirm confirm)) {
47 | yield null;
48 | }
49 | yield new ContainerCraftingTree(player.inventory, (ITerminalHost) confirm.getTarget());
50 | }
51 | };
52 | }
53 |
54 | @Nullable
55 | @Override
56 | public Object getClientGuiElement(final int ID, final EntityPlayer player, final World world, final int x, final int y, final int z) {
57 | return null;
58 | }
59 |
60 | public enum GuiType {
61 |
62 | CRAFTING_TREE(),
63 |
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/container/ContainerCraftingTree.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.container;
2 |
3 | import appeng.api.networking.crafting.ICraftingJob;
4 | import appeng.api.storage.ITerminalHost;
5 | import appeng.container.AEBaseContainer;
6 | import net.minecraft.entity.player.InventoryPlayer;
7 |
8 | import java.util.concurrent.Future;
9 |
10 | public class ContainerCraftingTree extends AEBaseContainer {
11 |
12 | private Future job = null;
13 |
14 | public ContainerCraftingTree(final InventoryPlayer ip, final ITerminalHost te) {
15 | super(ip, te);
16 | }
17 |
18 | public void setJob(final Future job) {
19 | this.job = job;
20 | }
21 |
22 | public Future getJob() {
23 | return job;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/integration/JEIUtils.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.integration;
2 |
3 | import github.kasuminova.ae2ctl.AE2CTLegacy;
4 | import mezz.jei.Internal;
5 | import mezz.jei.api.IJeiHelpers;
6 | import mezz.jei.api.IJeiRuntime;
7 | import mezz.jei.api.IRecipeRegistry;
8 | import mezz.jei.api.ingredients.IIngredientRegistry;
9 | import mezz.jei.api.recipe.IStackHelper;
10 | import mezz.jei.bookmarks.BookmarkList;
11 | import mezz.jei.config.Config;
12 | import mezz.jei.input.InputHandler;
13 | import net.minecraft.item.ItemStack;
14 |
15 | import java.lang.reflect.Field;
16 |
17 | public class JEIUtils {
18 |
19 | public static Field inputHandler = null;
20 | public static Field bookmarkList = null;
21 |
22 | static {
23 | // I Just want to get the BookmarkList...
24 | try {
25 | Field inputHandler = Internal.class.getDeclaredField("inputHandler");
26 | inputHandler.setAccessible(true);
27 | JEIUtils.inputHandler = inputHandler;
28 |
29 | Field bookmarkList = InputHandler.class.getDeclaredField("bookmarkList");
30 | bookmarkList.setAccessible(true);
31 | JEIUtils.bookmarkList = bookmarkList;
32 | } catch (NoSuchFieldException e) {
33 | AE2CTLegacy.log.warn(e);
34 | }
35 | }
36 |
37 | public static IJeiRuntime getJeiRuntime() {
38 | return Internal.getRuntime();
39 | }
40 |
41 | public static void addItemStackToBookmarkList(ItemStack stack) {
42 | if (inputHandler == null || bookmarkList == null || stack.isEmpty()) {
43 | return;
44 | }
45 |
46 | try {
47 | InputHandler handler = (InputHandler) inputHandler.get(null);
48 | BookmarkList bookmark = (BookmarkList) bookmarkList.get(handler);
49 |
50 | if (!Config.isBookmarkOverlayEnabled()) {
51 | Config.toggleBookmarkEnabled();
52 | }
53 | bookmark.add(stack);
54 | } catch (IllegalAccessException e) {
55 | AE2CTLegacy.log.warn(e);
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/integration/ae2/data/LiteCraftTreeNode.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.integration.ae2.data;
2 |
3 | import appeng.api.storage.data.IAEItemStack;
4 | import appeng.crafting.CraftingTreeNode;
5 | import appeng.crafting.CraftingTreeProcess;
6 | import github.kasuminova.ae2ctl.common.util.AEItemStackSet;
7 | import github.kasuminova.ae2ctl.common.util.ByteBufUtils;
8 | import github.kasuminova.ae2ctl.mixin.ae2.AccessorCraftingTreeNode;
9 | import io.netty.buffer.ByteBuf;
10 |
11 | import javax.annotation.Nonnull;
12 | import java.util.ArrayList;
13 | import java.util.Comparator;
14 | import java.util.List;
15 | import java.util.Objects;
16 |
17 | public final class LiteCraftTreeNode implements Comparable {
18 | private final LiteCraftTreeProc parent;
19 | private final IAEItemStack output;
20 | private final List inputs;
21 | private final long missing;
22 |
23 | private boolean missingCached = false;
24 | private boolean missingCache = false;
25 |
26 | public LiteCraftTreeNode(final LiteCraftTreeProc parent, IAEItemStack output, List inputs, long missing) {
27 | this.parent = parent;
28 | this.output = output;
29 | this.inputs = inputs;
30 | this.missing = missing;
31 | }
32 |
33 | public static LiteCraftTreeNode of(final CraftingTreeNode node, final LiteCraftTreeProc parent) {
34 | AccessorCraftingTreeNode accessor = (AccessorCraftingTreeNode) node;
35 | List inputs = new ArrayList<>();
36 | for (CraftingTreeProcess process : accessor.getNodes()) {
37 | LiteCraftTreeProc proc = LiteCraftTreeProc.of(process);
38 | if (proc != null) {
39 | inputs.add(proc);
40 | }
41 | }
42 | return new LiteCraftTreeNode(parent, accessor.getWhat().copy().setCraftable(false), inputs, accessor.getMissing());
43 | }
44 |
45 | public static LiteCraftTreeNode fromBuffer(final ByteBuf buf, final AEItemStackSet stackSet, final LiteCraftTreeProc parent) {
46 | int stackID = (int) ByteBufUtils.readVarLong(buf);
47 | IAEItemStack output = stackSet.get(stackID);
48 |
49 | long stackSize = ByteBufUtils.readVarLong(buf);
50 | output.setStackSize(stackSize);
51 |
52 | int size = buf.readByte();
53 | List inputs = new ArrayList<>(size);
54 | for (int i = 0; i < size; i++) {
55 | inputs.add(LiteCraftTreeProc.fromBuffer(buf, stackSet));
56 | }
57 |
58 | long missing = ByteBufUtils.readVarLong(buf);
59 | return new LiteCraftTreeNode(parent, output, inputs, missing);
60 | }
61 |
62 | public void writeToBuffer(final ByteBuf buf, final AEItemStackSet stackSet) {
63 | if (inputs.size() > Byte.MAX_VALUE) {
64 | throw new IllegalStateException("Too many inputs for a single node");
65 | }
66 |
67 | int stackID = stackSet.add(output);
68 | ByteBufUtils.writeVarLong(buf, stackID);
69 |
70 | long stackSize = output.getStackSize();
71 | ByteBufUtils.writeVarLong(buf, stackSize);
72 |
73 | buf.writeByte(inputs.size());
74 | inputs.forEach(input -> input.writeToBuffer(buf, stackSet));
75 | ByteBufUtils.writeVarLong(buf, missing);
76 | }
77 |
78 | public void sort() {
79 | inputs.sort(Comparator.reverseOrder());
80 | for (final LiteCraftTreeProc input : inputs) {
81 | input.sort();
82 | for (final LiteCraftTreeNode subNode : input.inputs()) {
83 | subNode.sort();
84 | }
85 | }
86 | }
87 |
88 | @Override
89 | public int compareTo(@Nonnull final LiteCraftTreeNode o) {
90 | return Integer.compare(diveToDeep(this, 0, new DepthRecorder()), diveToDeep(o, 0, new DepthRecorder()));
91 | }
92 |
93 | public static int diveToDeep(final LiteCraftTreeNode node, final int depth, final DepthRecorder recorder) {
94 | for (final LiteCraftTreeProc input : node.inputs) {
95 | for (final LiteCraftTreeNode subNode : input.inputs()) {
96 | int newDepth = depth + 1;
97 | recorder.dive(newDepth);
98 | diveToDeep(subNode, newDepth, recorder);
99 | }
100 | }
101 | return recorder.getDepth();
102 | }
103 |
104 | public int totalProcessors() {
105 | int size = inputs.size();
106 | for (final LiteCraftTreeProc input : inputs) {
107 | for (final LiteCraftTreeNode node : input.inputs()) {
108 | size += node.totalProcessors();
109 | }
110 | }
111 | return size;
112 | }
113 |
114 | public int getRenderExpandNodes() {
115 | int size = Math.max(inputs.size() - 1, 0);
116 | for (final LiteCraftTreeProc input : inputs) {
117 | size += Math.max(input.inputs().size() - 1, 0);
118 | for (final LiteCraftTreeNode node : input.inputs()) {
119 | size += node.getRenderExpandNodes();
120 | }
121 | }
122 | return size;
123 | }
124 |
125 | public int getLastNodeRenderExpandNodes() {
126 | if (inputs.isEmpty()) {
127 | return 0;
128 | }
129 | LiteCraftTreeProc proc = inputs.get(inputs.size() - 1);
130 |
131 | List subNodes = proc.inputs();
132 | if (subNodes.isEmpty()) {
133 | return 0;
134 | }
135 |
136 | LiteCraftTreeNode subNode = subNodes.get(subNodes.size() - 1);
137 | return subNode.getRenderExpandNodes();
138 | }
139 |
140 | public LiteCraftTreeNode withMissingOnly() {
141 | if (!isMissing(this)) {
142 | return null;
143 | }
144 |
145 | List missingInputs = new ArrayList<>();
146 | for (final LiteCraftTreeProc input : inputs) {
147 | List missingSubNodes = new ArrayList<>();
148 | for (final LiteCraftTreeNode subNode : input.inputs()) {
149 | if (isMissing(subNode)) {
150 | missingSubNodes.add(subNode.withMissingOnly());
151 | }
152 | }
153 | if (!missingSubNodes.isEmpty()) {
154 | missingInputs.add(new LiteCraftTreeProc(missingSubNodes));
155 | }
156 | }
157 |
158 | LiteCraftTreeNode node = new LiteCraftTreeNode(parent, output, missingInputs, missing);
159 | node.missingCached = true;
160 | node.missingCache = true;
161 | return node;
162 | }
163 |
164 | public LiteCraftTreeProc parent() {
165 | return parent;
166 | }
167 |
168 | public IAEItemStack output() {
169 | return output;
170 | }
171 |
172 | public List inputs() {
173 | return inputs;
174 | }
175 |
176 | public long missing() {
177 | return missing;
178 | }
179 |
180 | /**
181 | * Check if this node or sub nodes is missing ingredients.
182 | */
183 | public static boolean isMissing(final LiteCraftTreeNode node) {
184 | if (node.missingCached) {
185 | return node.missingCache;
186 | }
187 | if (node.missing() > 0) {
188 | node.missingCached = true;
189 | return node.missingCache = true;
190 | }
191 | for (final LiteCraftTreeProc input : node.inputs()) {
192 | for (final LiteCraftTreeNode subNode : input.inputs()) {
193 | if (isMissing(subNode)) {
194 | return node.missingCached = node.missingCache = true;
195 | }
196 | }
197 | }
198 | node.missingCached = true;
199 | node.missingCache = false;
200 | return false;
201 | }
202 |
203 | @Override
204 | public boolean equals(Object obj) {
205 | if (obj == this) return true;
206 | if (obj == null || obj.getClass() != this.getClass()) return false;
207 | var that = (LiteCraftTreeNode) obj;
208 | return Objects.equals(this.output, that.output) &&
209 | Objects.equals(this.inputs, that.inputs) &&
210 | this.missing == that.missing;
211 | }
212 |
213 | @Override
214 | public int hashCode() {
215 | return Objects.hash(output, inputs, missing);
216 | }
217 |
218 | @Override
219 | public String toString() {
220 | return "LiteCraftTreeNode[" +
221 | "output=" + output + ", " +
222 | "inputs=" + inputs + ", " +
223 | "missing=" + missing + ']';
224 | }
225 |
226 | public static class DepthRecorder {
227 |
228 | private int depth;
229 |
230 | void dive(int depth) {
231 | this.depth = Math.max(this.depth, depth);
232 | }
233 |
234 | public int getDepth() {
235 | return depth;
236 | }
237 |
238 | }
239 |
240 | }
241 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/integration/ae2/data/LiteCraftTreeProc.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.integration.ae2.data;
2 |
3 | import appeng.crafting.CraftingTreeNode;
4 | import appeng.crafting.CraftingTreeProcess;
5 | import com.github.bsideup.jabel.Desugar;
6 | import github.kasuminova.ae2ctl.common.util.AEItemStackSet;
7 | import github.kasuminova.ae2ctl.common.util.CraftingTreeProcessUtil;
8 | import io.netty.buffer.ByteBuf;
9 |
10 | import javax.annotation.Nonnull;
11 | import javax.annotation.Nullable;
12 | import java.util.ArrayList;
13 | import java.util.Comparator;
14 | import java.util.List;
15 |
16 | @Desugar
17 | public record LiteCraftTreeProc(List inputs) implements Comparable {
18 |
19 | @Nullable
20 | public static LiteCraftTreeProc of(final CraftingTreeProcess process) {
21 | List inputs = new ArrayList<>();
22 | LiteCraftTreeProc proc = new LiteCraftTreeProc(inputs);
23 | for (CraftingTreeNode node : CraftingTreeProcessUtil.getNodes(process).keySet()) {
24 | inputs.add(LiteCraftTreeNode.of(node, proc));
25 | }
26 | // return null if no inputs
27 | return inputs.isEmpty() ? null : proc;
28 | }
29 |
30 | public static LiteCraftTreeProc fromBuffer(final ByteBuf buf, final AEItemStackSet stackSet) {
31 | int size = buf.readByte();
32 | List inputs = new ArrayList<>();
33 | LiteCraftTreeProc proc = new LiteCraftTreeProc(inputs);
34 | for (int i = 0; i < size; i++) {
35 | inputs.add(LiteCraftTreeNode.fromBuffer(buf, stackSet, proc));
36 | }
37 | return proc;
38 | }
39 |
40 | public void writeToBuffer(final ByteBuf buf, final AEItemStackSet stackSet) {
41 | if (inputs.size() > Byte.MAX_VALUE) {
42 | throw new IllegalStateException("Too many inputs for a single node");
43 | }
44 | buf.writeByte(inputs.size());
45 | inputs.forEach(node -> node.writeToBuffer(buf, stackSet));
46 | }
47 |
48 | public void sort() {
49 | inputs.sort(Comparator.reverseOrder());
50 | for (final LiteCraftTreeNode input : inputs) {
51 | for (final LiteCraftTreeProc proc : input.inputs()) {
52 | proc.sort();
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public int compareTo(@Nonnull final LiteCraftTreeProc o) {
59 | return Integer.compare(diveToDeep(this, 0, new LiteCraftTreeNode.DepthRecorder()), diveToDeep(o, 0, new LiteCraftTreeNode.DepthRecorder()));
60 | }
61 |
62 | public static int diveToDeep(final LiteCraftTreeProc proc, final int depth, final LiteCraftTreeNode.DepthRecorder recorder) {
63 | for (final LiteCraftTreeNode node : proc.inputs) {
64 | for (final LiteCraftTreeProc subProc : node.inputs()) {
65 | int newDepth = depth + 1;
66 | recorder.dive(newDepth);
67 | diveToDeep(subProc, newDepth, recorder);
68 | }
69 | }
70 | return recorder.getDepth();
71 | }
72 |
73 | public int totalNodes() {
74 | int nodeCount = inputs.size();
75 | for (final LiteCraftTreeNode node : inputs) {
76 | for (final LiteCraftTreeProc input : node.inputs()) {
77 | nodeCount += input.totalNodes();
78 | }
79 | }
80 | return nodeCount;
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/mod/Mods.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.mod;
2 |
3 | import net.minecraftforge.fml.common.Loader;
4 |
5 | public enum Mods {
6 |
7 | JEI("jei"),
8 | AE2("appliedenergistics2"),
9 |
10 | ;
11 | final String modID;
12 | final String requiredClass;
13 | boolean loaded = false;
14 | boolean initialized = false;
15 |
16 | Mods(final String modID) {
17 | this.modID = modID;
18 | this.requiredClass = null;
19 | }
20 |
21 | Mods(final String modID, final String requiredClass) {
22 | this.modID = modID;
23 | this.requiredClass = requiredClass;
24 | }
25 |
26 | public boolean loaded() {
27 | if (initialized) {
28 | return loaded;
29 | }
30 |
31 | initialized = true;
32 |
33 | if (requiredClass != null) {
34 | try {
35 | Class.forName(requiredClass);
36 | return loaded = true;
37 | } catch (Throwable e) {
38 | return loaded = false;
39 | }
40 | }
41 | return loaded = Loader.isModLoaded(modID);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/network/PktCraftingTreeData.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.network;
2 |
3 | import appeng.crafting.CraftingTreeNode;
4 | import github.kasuminova.ae2ctl.client.gui.GuiCraftingTree;
5 | import github.kasuminova.ae2ctl.common.integration.ae2.data.LiteCraftTreeNode;
6 | import github.kasuminova.ae2ctl.common.util.AEItemStackSet;
7 | import io.netty.buffer.ByteBuf;
8 | import io.netty.buffer.Unpooled;
9 | import net.minecraft.client.Minecraft;
10 | import net.minecraft.client.gui.GuiScreen;
11 | import net.minecraftforge.fml.common.FMLCommonHandler;
12 | import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
13 | import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
14 | import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
15 | import net.minecraftforge.fml.relauncher.Side;
16 | import net.minecraftforge.fml.relauncher.SideOnly;
17 |
18 | public class PktCraftingTreeData implements IMessage, IMessageHandler {
19 |
20 | private LiteCraftTreeNode root = null;
21 |
22 | public PktCraftingTreeData() {
23 | }
24 |
25 | public PktCraftingTreeData(final CraftingTreeNode root) {
26 | this.root = LiteCraftTreeNode.of(root, null);
27 | }
28 |
29 | @Override
30 | public void fromBytes(final ByteBuf buf) {
31 | AEItemStackSet stackSet = new AEItemStackSet();
32 |
33 | // Read
34 | stackSet.fromBuffer(buf);
35 | root = LiteCraftTreeNode.fromBuffer(buf, stackSet, null);
36 | }
37 |
38 | @Override
39 | public void toBytes(final ByteBuf buf) {
40 | AEItemStackSet stackSet = new AEItemStackSet();
41 |
42 | // PreWrite
43 | ByteBuf buffer = Unpooled.buffer();
44 | root.writeToBuffer(buffer, stackSet);
45 |
46 | // Write
47 | stackSet.writeToBuffer(buf);
48 | buf.writeBytes(buffer);
49 | }
50 |
51 | @Override
52 | public IMessage onMessage(final PktCraftingTreeData message, final MessageContext ctx) {
53 | if (FMLCommonHandler.instance().getSide().isClient()) {
54 | Minecraft.getMinecraft().addScheduledTask(() -> processPacket(message));
55 | }
56 | return null;
57 | }
58 |
59 | @SideOnly(Side.CLIENT)
60 | protected static void processPacket(final PktCraftingTreeData message) {
61 | LiteCraftTreeNode root = message.root;
62 | GuiScreen cur = Minecraft.getMinecraft().currentScreen;
63 | if (!(cur instanceof GuiCraftingTree treeGUI)) {
64 | return;
65 | }
66 |
67 | treeGUI.onDataUpdate(root);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/network/PktSwitchCraftingTree.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.network;
2 |
3 | import appeng.api.networking.crafting.ICraftingJob;
4 | import appeng.api.networking.security.IActionHost;
5 | import appeng.container.ContainerOpenContext;
6 | import appeng.container.implementations.ContainerCraftConfirm;
7 | import appeng.container.interfaces.IInventorySlotAware;
8 | import appeng.core.sync.GuiBridge;
9 | import appeng.crafting.CraftingJob;
10 | import appeng.crafting.CraftingTreeNode;
11 | import appeng.util.Platform;
12 | import github.kasuminova.ae2ctl.AE2CTLegacy;
13 | import github.kasuminova.ae2ctl.common.CommonProxy;
14 | import github.kasuminova.ae2ctl.common.container.ContainerCraftingTree;
15 | import github.kasuminova.ae2ctl.mixin.ae2.AccessorContainerCraftConfirm;
16 | import io.netty.buffer.ByteBuf;
17 | import net.minecraft.entity.player.EntityPlayerMP;
18 | import net.minecraft.tileentity.TileEntity;
19 | import net.minecraftforge.fml.common.FMLCommonHandler;
20 | import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
21 | import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
22 | import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
23 |
24 | import java.util.concurrent.FutureTask;
25 |
26 | public class PktSwitchCraftingTree implements IMessage, IMessageHandler {
27 |
28 | public PktSwitchCraftingTree() {
29 | }
30 |
31 | @Override
32 | public void fromBytes(final ByteBuf buf) {
33 | }
34 |
35 | @Override
36 | public void toBytes(final ByteBuf buf) {
37 | }
38 |
39 | @Override
40 | public IMessage onMessage(final PktSwitchCraftingTree message, final MessageContext ctx) {
41 | EntityPlayerMP player = ctx.getServerHandler().player;
42 | FMLCommonHandler.instance().getMinecraftServerInstance().addScheduledTask(() -> {
43 | if (player.openContainer instanceof ContainerCraftConfirm confirm) {
44 | processConfirmGUI(confirm, player);
45 | } else if (player.openContainer instanceof ContainerCraftingTree craftingTree) {
46 | processTreeGUI(craftingTree, player);
47 | }
48 | });
49 | return null;
50 | }
51 |
52 | private static void processTreeGUI(final ContainerCraftingTree craftingTree, final EntityPlayerMP player) {
53 | ContainerOpenContext context = craftingTree.getOpenContext();
54 | final TileEntity te = context.getTile();
55 | final Object target = craftingTree.getTarget();
56 | if (target instanceof IActionHost ah) {
57 | if (te != null) {
58 | Platform.openGUI(player, te, context.getSide(), GuiBridge.GUI_CRAFTING_CONFIRM);
59 | } else if (ah instanceof IInventorySlotAware slotAware) {
60 | Platform.openGUI(player, slotAware.getInventorySlot(), GuiBridge.GUI_CRAFTING_CONFIRM, slotAware.isBaubleSlot());
61 | }
62 |
63 | if (player.openContainer instanceof ContainerCraftConfirm confirm) {
64 | confirm.setJob(craftingTree.getJob());
65 | confirm.detectAndSendChanges();
66 | }
67 | }
68 | }
69 |
70 | private static void processConfirmGUI(final ContainerCraftConfirm confirm, final EntityPlayerMP player) {
71 | AccessorContainerCraftConfirm accessor = (AccessorContainerCraftConfirm) confirm;
72 | ICraftingJob result = accessor.getResult();
73 | if (result == null) {
74 | return;
75 | }
76 |
77 | CraftingTreeNode tree;
78 | if (!(result instanceof CraftingJob job)) {
79 | return;
80 | }
81 | tree = job.getTree();
82 |
83 | player.openGui(AE2CTLegacy.MOD_ID, CommonProxy.GuiType.CRAFTING_TREE.ordinal(), player.world, (int) player.posX, (int) player.posY, (int) player.posZ);
84 | if (!(player.openContainer instanceof ContainerCraftingTree craftingTree)) {
85 | return;
86 | }
87 |
88 | craftingTree.setOpenContext(confirm.getOpenContext());
89 | FutureTask fakeFuture = new FutureTask<>(() -> result);
90 | craftingTree.setJob(fakeFuture);
91 | fakeFuture.run();
92 | AE2CTLegacy.NET_CHANNEL.sendTo(new PktCraftingTreeData(tree), player);
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/util/AEItemStackSet.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.util;
2 |
3 | import appeng.api.storage.data.IAEItemStack;
4 | import appeng.util.item.AEItemStack;
5 | import com.github.bsideup.jabel.Desugar;
6 | import io.netty.buffer.ByteBuf;
7 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
8 | import it.unimi.dsi.fastutil.objects.ObjectArrayList;
9 |
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class AEItemStackSet {
14 |
15 | private final Map entries = new Object2ObjectOpenHashMap<>();
16 | private final List entryList = new ObjectArrayList<>();
17 |
18 | public int add(IAEItemStack stack) {
19 | Entry entry = entries.get(new Entry(stack, -1));
20 | if (entry == null) {
21 | entry = new Entry(stack.copy().setStackSize(1), entryList.size());
22 | entries.put(entry, entry);
23 | entryList.add(entry);
24 | }
25 | return entry.id();
26 | }
27 |
28 | protected void addInternal(IAEItemStack stack) {
29 | Entry entry = new Entry(stack, entryList.size());
30 | entryList.add(entry);
31 | entries.put(entry, entry);
32 | }
33 |
34 | public IAEItemStack get(int id) {
35 | return entryList.get(id).stack().copy();
36 | }
37 |
38 | public void writeToBuffer(final ByteBuf buf) {
39 | buf.writeInt(entryList.size());
40 | for (Entry entry : entryList) {
41 | try {
42 | entry.stack().writeToPacket(buf);
43 | } catch (Throwable ignored) {
44 | }
45 | }
46 | }
47 |
48 | public void fromBuffer(final ByteBuf buf) {
49 | int size = buf.readInt();
50 | for (int i = 0; i < size; i++) {
51 | addInternal(AEItemStack.fromPacket(buf));
52 | }
53 | }
54 |
55 | @Desugar
56 | private record Entry(IAEItemStack stack, int id) {
57 |
58 | @Override
59 | public int hashCode() {
60 | return stack.hashCode();
61 | }
62 |
63 | @Override
64 | public boolean equals(final Object obj) {
65 | if (!(obj instanceof Entry entry)) {
66 | return false;
67 | }
68 | return stack.equals(entry.stack);
69 | }
70 |
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/util/ByteBufUtils.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.util;
2 |
3 | import io.netty.buffer.ByteBuf;
4 |
5 | public class ByteBufUtils {
6 |
7 | private static final short SHORT = (0xFF - 1);
8 | private static final short INT = (0xFF - 2);
9 | private static final short LONG = (0xFF - 3);
10 |
11 | private static final long UINT_MAX = 0xFFFFFFFFL;
12 | private static final int USHORT_MAX = 0xFFFF;
13 | private static final short UBYTE_MAX = 0xFF - 4;
14 |
15 | public static void writeVarLong(ByteBuf buf, long value) {
16 | if (value > UINT_MAX) {
17 | writeUnsignedByte(buf, LONG);
18 | buf.writeLong(value);
19 | return;
20 | }
21 | if (value > USHORT_MAX) {
22 | writeUnsignedByte(buf, INT);
23 | writeUnsignedInt(buf, value);
24 | return;
25 | }
26 | if (value > UBYTE_MAX) {
27 | writeUnsignedByte(buf, SHORT);
28 | writeUnsignedShort(buf, (int) value);
29 | return;
30 | }
31 | writeUnsignedByte(buf, (short) value);
32 | }
33 |
34 | public static long readVarLong(ByteBuf buf) {
35 | short type = buf.readUnsignedByte();
36 | return switch (type) {
37 | case SHORT -> buf.readUnsignedShort();
38 | case INT -> buf.readUnsignedInt();
39 | case LONG -> buf.readLong();
40 | default -> type;
41 | };
42 | }
43 |
44 | public static void writeUnsignedByte(ByteBuf buf, short value) {
45 | if (value < 0 || value > 0xFF) {
46 | throw new IllegalArgumentException("Value out of range for unsigned byte: " + value);
47 | }
48 | buf.writeByte((byte) value);
49 | }
50 |
51 | public static void writeUnsignedShort(ByteBuf buf, int value) {
52 | if (value < 0 || value > 0xFFFF) {
53 | throw new IllegalArgumentException("Value out of range for unsigned short: " + value);
54 | }
55 | buf.writeShort((short) value);
56 | }
57 |
58 | public static void writeUnsignedInt(ByteBuf buf, long value) {
59 | if (value < 0 || value > 0xFFFFFFFFL) {
60 | throw new IllegalArgumentException("Value out of range for unsigned int: " + value);
61 | }
62 | buf.writeInt((int) value);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/util/CraftingTreeProcessUtil.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.util;
2 |
3 | import appeng.crafting.CraftingTreeNode;
4 | import appeng.crafting.CraftingTreeProcess;
5 | import github.kasuminova.ae2ctl.AE2CTLegacy;
6 |
7 | import java.lang.invoke.MethodHandle;
8 | import java.lang.invoke.MethodHandles;
9 | import java.lang.reflect.Field;
10 | import java.util.Map;
11 |
12 | public class CraftingTreeProcessUtil {
13 |
14 | private static final MethodHandle nodesHandle;
15 |
16 | static {
17 | try {
18 | Field nodes = CraftingTreeProcess.class.getDeclaredField("nodes");
19 | nodes.setAccessible(true);
20 | nodesHandle = MethodHandles.lookup().unreflectGetter(nodes);
21 | } catch (Throwable e) {
22 | AE2CTLegacy.log.fatal("[AE2CTL-FATAL] Failed to initialize CraftingTreeProcessUtil!", e);
23 | throw new RuntimeException(e);
24 | }
25 | }
26 |
27 | public static Map getNodes(final CraftingTreeProcess process) {
28 | try {
29 | return (Map) nodesHandle.invoke(process);
30 | } catch (Throwable e) {
31 | AE2CTLegacy.log.fatal("[AE2CTL-FATAL] Failed to get nodes from CraftingTreeProcess!", e);
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/common/util/NumberUtils.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.common.util;
2 |
3 | import java.text.DecimalFormat;
4 |
5 | public class NumberUtils {
6 |
7 | private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.##");
8 |
9 | public static String formatNumberToInt(long value) {
10 | if (value < 1_000L) {
11 | return String.valueOf(value);
12 | } else if (value < 1_000_000L) {
13 | return value / 1_000 + "K";
14 | } else if (value < 1_000_000_000L) {
15 | return value / 1_000_000 + "M";
16 | } else if (value < 1_000_000_000_000L) {
17 | return value / 1_000_000_000L + "G";
18 | } else if (value < 1_000_000_000_000_000L) {
19 | return value / 1_000_000_000_000L + "T";
20 | } else if (value < 1_000_000_000_000_000_000L) {
21 | return value / 1_000_000_000_000_000L + "P";
22 | } else {
23 | return value / 1_000_000_000_000_000_000L + "E";
24 | }
25 | }
26 |
27 | public static String formatDecimal(double value) {
28 | return DECIMAL_FORMAT.format(value);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/mixin/AE2CTLLateMixinLoader.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.mixin;
2 |
3 | import zone.rong.mixinbooter.ILateMixinLoader;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | @SuppressWarnings("unused")
9 | public class AE2CTLLateMixinLoader implements ILateMixinLoader {
10 |
11 | @Override
12 | public List getMixinConfigs() {
13 | return Collections.singletonList("mixins.ae2ctl.json");
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/mixin/ae2/AccessorContainerCraftConfirm.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.mixin.ae2;
2 |
3 | import appeng.api.networking.crafting.ICraftingJob;
4 | import appeng.container.implementations.ContainerCraftConfirm;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.gen.Accessor;
7 |
8 | @Mixin(value = ContainerCraftConfirm.class, remap = false)
9 | public interface AccessorContainerCraftConfirm {
10 |
11 | @Accessor
12 | ICraftingJob getResult();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/mixin/ae2/AccessorCraftingTreeNode.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.mixin.ae2;
2 |
3 | import appeng.api.storage.data.IAEItemStack;
4 | import appeng.crafting.CraftingTreeNode;
5 | import appeng.crafting.CraftingTreeProcess;
6 | import org.spongepowered.asm.mixin.Mixin;
7 | import org.spongepowered.asm.mixin.gen.Accessor;
8 |
9 | import java.util.ArrayList;
10 |
11 | @Mixin(value = CraftingTreeNode.class, remap = false)
12 | public interface AccessorCraftingTreeNode {
13 |
14 | @Accessor
15 | ArrayList getNodes();
16 |
17 | @Accessor
18 | IAEItemStack getWhat();
19 |
20 | @Accessor
21 | long getMissing();
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/github/kasuminova/ae2ctl/mixin/ae2/MixinGuiCraftConfirm.java:
--------------------------------------------------------------------------------
1 | package github.kasuminova.ae2ctl.mixin.ae2;
2 |
3 | import appeng.client.gui.AEBaseGui;
4 | import appeng.client.gui.implementations.GuiCraftConfirm;
5 | import github.kasuminova.ae2ctl.AE2CTLegacy;
6 | import github.kasuminova.ae2ctl.client.gui.widget.vanilla.GuiButtonImageExt;
7 | import github.kasuminova.ae2ctl.common.network.PktSwitchCraftingTree;
8 | import net.minecraft.client.gui.GuiButton;
9 | import net.minecraft.client.resources.I18n;
10 | import net.minecraft.util.ResourceLocation;
11 | import org.spongepowered.asm.mixin.Mixin;
12 | import org.spongepowered.asm.mixin.Unique;
13 | import org.spongepowered.asm.mixin.injection.At;
14 | import org.spongepowered.asm.mixin.injection.Inject;
15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
16 |
17 | @Mixin(GuiCraftConfirm.class)
18 | public abstract class MixinGuiCraftConfirm extends AEBaseGui {
19 |
20 | @Unique
21 | private GuiButtonImageExt ae2ctl$craftTree;
22 |
23 | public MixinGuiCraftConfirm() {
24 | super(null);
25 | }
26 |
27 | @Inject(method = "initGui", at = @At("RETURN"))
28 | private void injectInitGui(final CallbackInfo ci) {
29 | ae2ctl$craftTree = new GuiButtonImageExt(-1,
30 | (this.guiLeft + this.xSize) - 26, this.guiTop - 4, 26, 19,
31 | 0, 0, 19,
32 | new ResourceLocation(AE2CTLegacy.MOD_ID, "textures/gui/guicraftingtree_light.png"));
33 | ae2ctl$craftTree.setMessage(I18n.format("gui.crafting_tree.switch"));
34 | this.buttonList.add(this.ae2ctl$craftTree);
35 | }
36 |
37 | @Inject(method = "actionPerformed", at = @At("HEAD"), cancellable = true)
38 | private void injectActionPerformed(final GuiButton btn, final CallbackInfo ci) {
39 | if (btn == ae2ctl$craftTree) {
40 | AE2CTLegacy.NET_CHANNEL.sendToServer(new PktSwitchCraftingTree());
41 | ci.cancel();
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/lang/en_US.lang:
--------------------------------------------------------------------------------
1 | gui.crafting_tree.title=Crafting Tree Preview
2 | gui.crafting_tree.switch=View Crafting Tree
3 | gui.crafting_tree.sub_node_missing=§cIngredients Insufficient.§f
4 | gui.crafting_tree.missing=§cMissing: §e%s§f
5 | gui.crafting_tree.screenshot=Screenshot Crafting Tree §c(Not Implemented)§f
6 | gui.crafting_tree.missing_only=Click to display only nodes with missing materials.
7 | gui.crafting_tree.default=Click to return to default view.
8 | gui.crafting_tree.back=Back
9 | gui.crafting_tree.tip.0=Click a node to select it.
10 | gui.crafting_tree.tip.1=Click empty space to deselect, use arrow keys to navigate.
11 | gui.crafting_tree.tip.2=Hold CTRL to move between missing items (← → Only).
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/lang/zh_CN.lang:
--------------------------------------------------------------------------------
1 | gui.crafting_tree.title=合成树预览
2 | gui.crafting_tree.switch=查看合成树
3 | gui.crafting_tree.sub_node_missing=§c材料不足。§f
4 | gui.crafting_tree.missing=§c缺失:§e%s§f
5 | gui.crafting_tree.screenshot=截图合成树§c(未实装)§f
6 | gui.crafting_tree.missing_only=单击只显示缺失材料的节点。
7 | gui.crafting_tree.default=单击返回默认视图。
8 | gui.crafting_tree.back=返回
9 | gui.crafting_tree.tip.0=单击节点以选中。
10 | gui.crafting_tree.tip.1=单击空白处取消选中,使用 ↑ ↓ ← → 键在选中的节点间移动。
11 | gui.crafting_tree.tip.2=按住 CTRL 时,只在缺失物品的节点间移动(仅限 ← → 键)。
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_256x256_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_256x256_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_256x256_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_256x256_light.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_320x256_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_320x256_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_320x256_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_320x256_light.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_384x320_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_384x320_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_384x320_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_384x320_light.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_512x320_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_512x320_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_512x320_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_512x320_light.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_640x384_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_640x384_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_640x384_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_640x384_light.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_dark.png
--------------------------------------------------------------------------------
/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovaEngineering-Source/AE2CT-Legacy/99ab83936ab9d0a51f93de91f76b05a839aedf5f/src/main/resources/assets/ae2ctl/textures/gui/guicraftingtree_light.png
--------------------------------------------------------------------------------
/src/main/resources/mcmod.info:
--------------------------------------------------------------------------------
1 | {
2 | "modListVersion": 2,
3 | "modList": [
4 | {
5 | "modid": "ae2ctl",
6 | "name": "AE2 Crafting Tree - Legacy",
7 | "description": "AE2 Crafting Tree, for version 1.12.2.",
8 | "version": "0.1.0",
9 | "mcversion": "1.12.2",
10 | "url": "https://github.com/NovaEngineering-Source/AE2CTL",
11 | "updateUrl": "",
12 | "authorList": [
13 | "Kasumi_Nova"
14 | ],
15 | "credits": "",
16 | "logoFile": "",
17 | "screenshots": [],
18 | "dependencies": [],
19 | "requiredMods": [],
20 | "useDependencyInformation": true
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/src/main/resources/mixins.ae2ctl.json:
--------------------------------------------------------------------------------
1 | {
2 | "package": "github.kasuminova.ae2ctl.mixin.ae2",
3 | "refmap": "mixins.ae2ctl.refmap.json",
4 | "target": "@env(DEFAULT)",
5 | "minVersion": "0.8",
6 | "compatibilityLevel": "JAVA_8",
7 | "client": [
8 | "MixinGuiCraftConfirm"
9 | ],
10 | "mixins": [
11 | "AccessorContainerCraftConfirm",
12 | "AccessorCraftingTreeNode"
13 | ]
14 | }
--------------------------------------------------------------------------------