aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaven Szewczyk <git@eigenraven.me>2024-05-23 17:17:54 +0100
committerRaven Szewczyk <git@eigenraven.me>2024-05-23 17:17:54 +0100
commit8f3f5f67388cf3319da2a7e31e1c47b0c654d81b (patch)
tree4b3cb1e4ac87ecd167d0aea4d7a1086991e86b13
parent5150fdf3b3d86c8405d15435f26a8515f92d7492 (diff)
parent64de1cb440a4ee5e8c3afa83c3c024f3355a018b (diff)
downloadGT5-Unofficial-8f3f5f67388cf3319da2a7e31e1c47b0c654d81b.tar.gz
GT5-Unofficial-8f3f5f67388cf3319da2a7e31e1c47b0c654d81b.tar.bz2
GT5-Unofficial-8f3f5f67388cf3319da2a7e31e1c47b0c654d81b.zip
Merge in GigaGramFab with history
git-subtree-dir: gigagramfab git-subtree-mainline: 5150fdf3b3d86c8405d15435f26a8515f92d7492 git-subtree-split: 64de1cb440a4ee5e8c3afa83c3c024f3355a018b
-rw-r--r--gigagramfab/.git-blame-ignore-revs2
-rw-r--r--gigagramfab/.gitattributes44
-rw-r--r--gigagramfab/.github/test-scala-presence.toml2
-rw-r--r--gigagramfab/.github/workflows/build-and-test.yml13
-rw-r--r--gigagramfab/.github/workflows/release-tags.yml14
-rw-r--r--gigagramfab/.github/workflows/test-scala-presence.yml18
-rw-r--r--gigagramfab/.gitignore38
-rw-r--r--gigagramfab/LICENSE661
-rw-r--r--gigagramfab/README.md6
-rw-r--r--gigagramfab/addon.gradle6
-rw-r--r--gigagramfab/build.gradle5
-rw-r--r--gigagramfab/dependencies.gradle8
-rw-r--r--gigagramfab/gradle.properties192
-rw-r--r--gigagramfab/gradle/wrapper/gradle-wrapper.jarbin0 -> 43453 bytes
-rw-r--r--gigagramfab/gradle/wrapper/gradle-wrapper.properties7
-rwxr-xr-xgigagramfab/gradlew249
-rw-r--r--gigagramfab/gradlew.bat92
-rw-r--r--gigagramfab/jitpack.yml2
-rw-r--r--gigagramfab/repositories.gradle5
-rw-r--r--gigagramfab/settings.gradle23
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/BlockIcons.java45
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/ComponentRecipeLoader.java46
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/ConfigurationHandler.java51
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/GGConstants.java14
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/GGItemList.java197
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/GigaGramFab.java169
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/SingleUseToolRecipeLoader.java99
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/api/GGFabRecipeMaps.java55
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/api/GigaGramFabAPI.java30
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/items/GGMetaItem_DumbItems.java153
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java1109
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_LinkedInputBus.java616
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/mui/ClickableTextWidget.java55
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/util/GGUtils.java75
-rw-r--r--gigagramfab/src/main/java/net/glease/ggfab/util/OverclockHelper.java75
-rw-r--r--gigagramfab/src/main/resources/LICENSE21
-rw-r--r--gigagramfab/src/main/resources/META-INF/ggfab_at.cfg2
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/lang/en_US.lang41
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/lang/zh_CN.lang28
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE.pngbin0 -> 382 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE.pngbin0 -> 389 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW.pngbin0 -> 379 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_GLOW.pngbin0 -> 378 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK.pngbin0 -> 330 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW.pngbin0 -> 219 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/0.pngbin0 -> 349 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/1.pngbin0 -> 186 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/2.pngbin0 -> 221 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/3.pngbin0 -> 270 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/30.pngbin0 -> 413 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/31.pngbin0 -> 377 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/32.pngbin0 -> 372 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/33.pngbin0 -> 412 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/34.pngbin0 -> 393 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/35.pngbin0 -> 432 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/36.pngbin0 -> 418 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/4.pngbin0 -> 229 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/5.pngbin0 -> 284 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/6.pngbin0 -> 318 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT.pngbin0 -> 368 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE.pngbin0 -> 426 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE_GLOW.pngbin0 -> 363 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_GLOW.pngbin0 -> 141 bytes
-rw-r--r--gigagramfab/src/main/resources/mcmod.info18
-rw-r--r--gigagramfab/src/test/java/net/glease/ggfab/util/OverclockHelperTest.java47
77 files changed, 4333 insertions, 0 deletions
diff --git a/gigagramfab/.git-blame-ignore-revs b/gigagramfab/.git-blame-ignore-revs
new file mode 100644
index 0000000000..4cf91d237b
--- /dev/null
+++ b/gigagramfab/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# Ignore spotlessApply reformat
+d755802d116daddc62b47679d7f65490c4bdd7d6 \ No newline at end of file
diff --git a/gigagramfab/.gitattributes b/gigagramfab/.gitattributes
new file mode 100644
index 0000000000..fd2792b6cb
--- /dev/null
+++ b/gigagramfab/.gitattributes
@@ -0,0 +1,44 @@
+* text eol=lf
+
+*.[jJ][aA][rR] binary
+
+*.[pP][nN][gG] binary
+*.[jJ][pP][gG] binary
+*.[jJ][pP][eE][gG] binary
+*.[gG][iI][fF] binary
+*.[tT][iI][fF] binary
+*.[tT][iI][fF][fF] binary
+*.[iI][cC][oO] binary
+*.[sS][vV][gG] text
+*.[eE][pP][sS] binary
+*.[xX][cC][fF] binary
+
+*.[kK][aA][rR] binary
+*.[mM]4[aA] binary
+*.[mM][iI][dD] binary
+*.[mM][iI][dD][iI] binary
+*.[mM][pP]3 binary
+*.[oO][gG][gG] binary
+*.[rR][aA] binary
+
+*.7[zZ] binary
+*.[gG][zZ] binary
+*.[tT][aA][rR] binary
+*.[tT][gG][zZ] binary
+*.[zZ][iI][pP] binary
+
+*.[tT][cC][nN] binary
+*.[sS][oO] binary
+*.[dD][lL][lL] binary
+*.[dD][yY][lL][iI][bB] binary
+*.[pP][sS][dD] binary
+*.[tT][tT][fF] binary
+*.[oO][tT][fF] binary
+
+*.[pP][aA][tT][cC][hH] -text
+
+*.[bB][aA][tT] text eol=crlf
+*.[cC][mM][dD] text eol=crlf
+*.[pP][sS]1 text eol=crlf
+
+*[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary
diff --git a/gigagramfab/.github/test-scala-presence.toml b/gigagramfab/.github/test-scala-presence.toml
new file mode 100644
index 0000000000..ae0e9acd49
--- /dev/null
+++ b/gigagramfab/.github/test-scala-presence.toml
@@ -0,0 +1,2 @@
+[exclude]
+"src/main/java/**/*.java" = "import scala." \ No newline at end of file
diff --git a/gigagramfab/.github/workflows/build-and-test.yml b/gigagramfab/.github/workflows/build-and-test.yml
new file mode 100644
index 0000000000..3ee2f686fd
--- /dev/null
+++ b/gigagramfab/.github/workflows/build-and-test.yml
@@ -0,0 +1,13 @@
+
+name: Build and test
+
+on:
+ pull_request:
+ branches: [ master, main ]
+ push:
+ branches: [ master, main ]
+
+jobs:
+ build-and-test:
+ uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
+ secrets: inherit
diff --git a/gigagramfab/.github/workflows/release-tags.yml b/gigagramfab/.github/workflows/release-tags.yml
new file mode 100644
index 0000000000..e4c0be6b0d
--- /dev/null
+++ b/gigagramfab/.github/workflows/release-tags.yml
@@ -0,0 +1,14 @@
+
+name: Release tagged build
+
+on:
+ push:
+ tags: [ '*' ]
+
+permissions:
+ contents: write
+
+jobs:
+ release-tags:
+ uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master
+ secrets: inherit
diff --git a/gigagramfab/.github/workflows/test-scala-presence.yml b/gigagramfab/.github/workflows/test-scala-presence.yml
new file mode 100644
index 0000000000..6b1091e1fd
--- /dev/null
+++ b/gigagramfab/.github/workflows/test-scala-presence.yml
@@ -0,0 +1,18 @@
+name: Test Scala Presence
+
+on:
+ pull_request:
+ branches: [ master, main ]
+ push:
+ branches: [ master, main ]
+
+jobs:
+ test-scala-presence:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Check file content
+ uses: mattsb42-meta/not-grep@1.0.0
+ with:
+ config-file: ./.github/test-scala-presence.toml
diff --git a/gigagramfab/.gitignore b/gigagramfab/.gitignore
new file mode 100644
index 0000000000..5e80e0ae57
--- /dev/null
+++ b/gigagramfab/.gitignore
@@ -0,0 +1,38 @@
+.gradle
+.settings
+/.idea/
+/.vscode/
+/run/
+/build/
+/eclipse/
+.classpath
+.project
+/bin/
+/config/
+/crash-reports/
+/logs/
+options.txt
+/saves/
+usernamecache.json
+banned-ips.json
+banned-players.json
+eula.txt
+ops.json
+server.properties
+servers.dat
+usercache.json
+whitelist.json
+/out/
+*.iml
+*.ipr
+*.iws
+src/main/resources/mixins.*([!.]).json
+*.bat
+*.DS_Store
+!gradlew.bat
+.factorypath
+addon.local.gradle
+addon.local.gradle.kts
+addon.late.local.gradle
+addon.late.local.gradle.kts
+layout.json
diff --git a/gigagramfab/LICENSE b/gigagramfab/LICENSE
new file mode 100644
index 0000000000..0ad25db4bd
--- /dev/null
+++ b/gigagramfab/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.
diff --git a/gigagramfab/README.md b/gigagramfab/README.md
new file mode 100644
index 0000000000..4e1db508e7
--- /dev/null
+++ b/gigagramfab/README.md
@@ -0,0 +1,6 @@
+# GGFab
+
+THIS MOD DOES NOT HAVE AN API. EVERYTHING IS SUBJECT TO CHANGE WITHOUT NOTICE! DO NOT PROGRAM AGAINST ME!
+
+An API is planned, use patience!
+However, IDs and names would probably keep the same. \ No newline at end of file
diff --git a/gigagramfab/addon.gradle b/gigagramfab/addon.gradle
new file mode 100644
index 0000000000..6f39b0c545
--- /dev/null
+++ b/gigagramfab/addon.gradle
@@ -0,0 +1,6 @@
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+} \ No newline at end of file
diff --git a/gigagramfab/build.gradle b/gigagramfab/build.gradle
new file mode 100644
index 0000000000..e57a16f9f1
--- /dev/null
+++ b/gigagramfab/build.gradle
@@ -0,0 +1,5 @@
+//version: 1707058017
+
+plugins {
+ id 'com.gtnewhorizons.gtnhconvention'
+}
diff --git a/gigagramfab/dependencies.gradle b/gigagramfab/dependencies.gradle
new file mode 100644
index 0000000000..7e4a3c1934
--- /dev/null
+++ b/gigagramfab/dependencies.gradle
@@ -0,0 +1,8 @@
+// Add your dependencies here
+
+dependencies {
+ api("com.github.GTNewHorizons:GT5-Unofficial:5.09.45.153:dev")
+
+ testImplementation(platform('org.junit:junit-bom:5.8.2'))
+ testImplementation('org.junit.jupiter:junit-jupiter')
+}
diff --git a/gigagramfab/gradle.properties b/gigagramfab/gradle.properties
new file mode 100644
index 0000000000..d2e6ff01e8
--- /dev/null
+++ b/gigagramfab/gradle.properties
@@ -0,0 +1,192 @@
+# ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable.
+# LOCAL to test local config updates.
+gtnh.settings.blowdryerTag = 0.2.0
+
+# Human-readable mod name, available for mcmod.info population.
+modName = GigaGramFab
+
+# Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation.
+# Conventionally lowercase.
+modId = ggfab
+
+# Root package of the mod, used to find various classes in other properties,
+# mcmod.info substitution, enabling assertions in run tasks, etc.
+modGroup = net.glease.ggfab
+
+# Whether to use modGroup as the maven publishing group.
+# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
+useModGroupForPublishing = false
+
+# Updates your build.gradle and settings.gradle automatically whenever an update is available.
+autoUpdateBuildScript = false
+
+# Version of Minecraft to target
+minecraftVersion = 1.7.10
+
+# Version of Minecraft Forge to target
+forgeVersion = 10.13.4.1614
+
+# Specify an MCP channel for dependency deobfuscation and the deobfParams task.
+channel = stable
+
+# Specify an MCP mappings version for dependency deobfuscation and the deobfParams task.
+mappingsVersion = 12
+
+# Defines other MCP mappings for dependency deobfuscation.
+remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
+
+# Select a default username for testing your mod. You can always override this per-run by running
+# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
+developmentEnvironmentUserName = glease
+
+# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
+# See https://github.com/bsideup/jabel for details on how this works.
+enableModernJavaSyntax = true
+
+# Enables injecting missing generics into the decompiled source code for a better coding experience.
+# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
+enableGenericInjection = false
+
+# Generate a class with a String field for the mod version named as defined below.
+# If generateGradleTokenClass is empty or not missing, no such class will be generated.
+# If gradleTokenVersion is empty or missing, the field will not be present in the class.
+generateGradleTokenClass =
+
+# Name of the token containing the project's current version to generate/replace.
+gradleTokenVersion = GRADLETOKEN_VERSION
+
+# [DEPRECATED] Mod ID replacement token.
+gradleTokenModId =
+
+# [DEPRECATED] Mod name replacement token.
+gradleTokenModName =
+
+# [DEPRECATED] Mod Group replacement token.
+gradleTokenGroupName =
+
+# [DEPRECATED]
+# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
+# public static final String VERSION = "GRADLETOKEN_VERSION";
+# The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's
+# version in @Mod([...], version = VERSION, [...]).
+# Leave these properties empty to skip individual token replacements.
+replaceGradleTokenInFile = GGConstants.java
+
+# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can
+# leave this property empty.
+# Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api
+apiPackage =
+
+# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/
+# There can be multiple files in a space-separated list.
+# Example value: mymodid_at.cfg nei_at.cfg
+accessTransformersFile = ggfab_at.cfg
+
+# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
+usesMixins = false
+
+# Adds some debug arguments like verbose output and class export.
+usesMixinDebug = false
+
+# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
+mixinPlugin =
+
+# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
+mixinsPackage =
+
+# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
+# This parameter is for legacy compatibility only
+# Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin
+coreModClass =
+
+# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
+# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
+containsMixinsAndOrCoreModOnly = false
+
+# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins.
+forceEnableMixins = true
+
+# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your
+# responsibility to check the license and request permission for distribution if required.
+usesShadowedDependencies = false
+
+# If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access
+# their own classes, making the minimization unreliable.
+minimizeShadowedDependencies = true
+
+# If disabled, won't rename the shadowed classes.
+relocateShadowedDependencies = true
+
+# Adds the GTNH maven, CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
+includeWellKnownRepositories = true
+
+# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
+# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
+# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
+usesMavenPublishing = true
+
+# Maven repository to publish the mod to.
+# mavenPublishUrl = https\://nexus.gtnewhorizons.com/repository/releases/
+
+# Publishing to Modrinth requires you to set the MODRINTH_TOKEN environment variable to your current Modrinth API token.
+#
+# The project's ID on Modrinth. Can be either the slug or the ID.
+# Leave this empty if you don't want to publish to Modrinth.
+modrinthProjectId =
+
+# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth.
+# Syntax: scope1-type1:name1;scope2-type2:name2;...
+# Where scope can be one of [required, optional, incompatible, embedded],
+# type can be one of [project, version],
+# and the name is the Modrinth project or version slug/id of the other mod.
+# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
+# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
+modrinthRelations =
+
+# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
+#
+# The project's numeric ID on CurseForge. You can find this in the About Project box.
+# Leave this empty if you don't want to publish on CurseForge.
+curseForgeProjectId =
+
+# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
+# Syntax: type1:name1;type2:name2;...
+# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
+# and the name is the CurseForge project slug of the other mod.
+# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft
+# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
+curseForgeRelations =
+
+# Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older
+# projects. New projects should not use this parameter.
+# customArchiveBaseName =
+
+# Optional parameter to have the build automatically fail if an illegal version is used.
+# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
+# The check is ONLY performed if the version is a git tag.
+# Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+
+# versionPattern =
+
+# Uncomment to prevent the source code from being published.
+# noPublishedSources = true
+
+# Uncomment this to disable Spotless checks.
+# This should only be uncommented to keep it easier to sync with upstream/other forks.
+# That is, if there is no other active fork/upstream, NEVER change this.
+# disableSpotless = true
+
+# Uncomment this to disable Checkstyle checks (currently wildcard import check).
+# disableCheckstyle = true
+
+# Override the IDEA build type. Valid values are: "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle"
+# (force use delegated build).
+# This is meant to be set in $HOME/.gradle/gradle.properties.
+# e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be native build.
+# WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want!
+# Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you.
+# ideaOverrideBuildType = idea
+
+# Whether IDEA should run spotless checks when pressing the Build button.
+# This is meant to be set in $HOME/.gradle/gradle.properties.
+# ideaCheckSpotlessOnBuild = true
+
diff --git a/gigagramfab/gradle/wrapper/gradle-wrapper.jar b/gigagramfab/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..e6441136f3
--- /dev/null
+++ b/gigagramfab/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gigagramfab/gradle/wrapper/gradle-wrapper.properties b/gigagramfab/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..b82aa23a4f
--- /dev/null
+++ b/gigagramfab/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gigagramfab/gradlew b/gigagramfab/gradlew
new file mode 100755
index 0000000000..1aa94a4269
--- /dev/null
+++ b/gigagramfab/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gigagramfab/gradlew.bat b/gigagramfab/gradlew.bat
new file mode 100644
index 0000000000..25da30dbde
--- /dev/null
+++ b/gigagramfab/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/gigagramfab/jitpack.yml b/gigagramfab/jitpack.yml
new file mode 100644
index 0000000000..09bbb514fc
--- /dev/null
+++ b/gigagramfab/jitpack.yml
@@ -0,0 +1,2 @@
+before_install:
+ - ./gradlew setupCIWorkspace \ No newline at end of file
diff --git a/gigagramfab/repositories.gradle b/gigagramfab/repositories.gradle
new file mode 100644
index 0000000000..033d07fda4
--- /dev/null
+++ b/gigagramfab/repositories.gradle
@@ -0,0 +1,5 @@
+// Add any additional repositories for your dependencies here
+
+repositories {
+ mavenLocal()
+}
diff --git a/gigagramfab/settings.gradle b/gigagramfab/settings.gradle
new file mode 100644
index 0000000000..94c2daf35c
--- /dev/null
+++ b/gigagramfab/settings.gradle
@@ -0,0 +1,23 @@
+
+pluginManagement {
+ repositories {
+ maven {
+ // RetroFuturaGradle
+ name "GTNH Maven"
+ url "https://nexus.gtnewhorizons.com/repository/public/"
+ mavenContent {
+ includeGroup("com.gtnewhorizons")
+ includeGroupByRegex("com\\.gtnewhorizons\\..+")
+ }
+ }
+ gradlePluginPortal()
+ mavenCentral()
+ mavenLocal()
+ }
+}
+
+plugins {
+ id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.22'
+}
+
+
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/BlockIcons.java b/gigagramfab/src/main/java/net/glease/ggfab/BlockIcons.java
new file mode 100644
index 0000000000..638b206f44
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/BlockIcons.java
@@ -0,0 +1,45 @@
+package net.glease.ggfab;
+
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.ResourceLocation;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.IIconContainer;
+
+public enum BlockIcons implements IIconContainer, Runnable {
+
+ OVERLAY_FRONT_ADV_ASSLINE_ACTIVE,
+ OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW,
+ OVERLAY_FRONT_ADV_ASSLINE_STUCK,
+ OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW,
+ OVERLAY_FRONT_ADV_ASSLINE,
+ OVERLAY_FRONT_ADV_ASSLINE_GLOW,;
+
+ public static final String RES_PATH = GGConstants.MODID + ":";
+ private IIcon mIcon;
+
+ BlockIcons() {
+ GregTech_API.sGTBlockIconload.add(this);
+ }
+
+ @Override
+ public IIcon getIcon() {
+ return mIcon;
+ }
+
+ @Override
+ public IIcon getOverlayIcon() {
+ return null;
+ }
+
+ @Override
+ public ResourceLocation getTextureFile() {
+ return TextureMap.locationBlocksTexture;
+ }
+
+ @Override
+ public void run() {
+ mIcon = GregTech_API.sBlockIcons.registerIcon(RES_PATH + "iconsets/" + this);
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/ComponentRecipeLoader.java b/gigagramfab/src/main/java/net/glease/ggfab/ComponentRecipeLoader.java
new file mode 100644
index 0000000000..088ad86025
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/ComponentRecipeLoader.java
@@ -0,0 +1,46 @@
+package net.glease.ggfab;
+
+import static gregtech.api.enums.GT_Values.RA;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+class ComponentRecipeLoader implements Runnable {
+
+ @Override
+ public void run() {
+ Fluid solderIndalloy = FluidRegistry.getFluid("molten.indalloy140") != null
+ ? FluidRegistry.getFluid("molten.indalloy140")
+ : FluidRegistry.getFluid("molten.solderingalloy");
+ RA.addAssemblylineRecipe(
+ ItemList.Machine_Multi_Assemblyline.get(1L),
+ 96000,
+ new Object[] { ItemList.Machine_Multi_Assemblyline.get(1L),
+ new Object[] { OrePrefixes.circuit.get(Materials.Master), 2 },
+ new Object[] { OrePrefixes.circuit.get(Materials.Elite), 4 },
+ new Object[] { OrePrefixes.circuit.get(Materials.Data), 8 },
+ ItemList.Automation_ChestBuffer_LuV.get(1L), },
+ new FluidStack[] { new FluidStack(solderIndalloy, 1296), Materials.Lubricant.getFluid(2000) },
+ GGItemList.AdvAssLine.get(1L),
+ 1200,
+ 6000);
+ RA.addAssemblerRecipe(
+ new ItemStack[] { ItemList.Hatch_Input_Bus_IV.get(1L), ItemList.Emitter_IV.get(1L),
+ ItemList.Sensor_IV.get(1L),
+ GT_OreDictUnificator.get(OrePrefixes.plateDense, Materials.Enderium, 1L),
+ GT_Utility.getIntegratedCircuit(12), },
+ Materials.Polybenzimidazole.getMolten(144L),
+ GGItemList.LinkedInputBus.get(1L),
+ 600,
+ (int) GT_Values.VP[5]);
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/ConfigurationHandler.java b/gigagramfab/src/main/java/net/glease/ggfab/ConfigurationHandler.java
new file mode 100644
index 0000000000..249bb16da8
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/ConfigurationHandler.java
@@ -0,0 +1,51 @@
+package net.glease.ggfab;
+
+import java.io.File;
+import java.util.Map;
+
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.common.config.Property;
+
+public enum ConfigurationHandler {
+
+ INSTANCE;
+
+ private Configuration config;
+ private float laserOCPenaltyFactor;
+
+ void init(File f) {
+ config = new Configuration(f);
+ loadConfig();
+ setLanguageKeys();
+ }
+
+ private void setLanguageKeys() {
+ for (String categoryName : config.getCategoryNames()) {
+ ConfigCategory category = config.getCategory(categoryName);
+ category.setLanguageKey("ggfab.config." + categoryName);
+ for (Map.Entry<String, Property> entry : category.entrySet()) {
+ entry.getValue().setLanguageKey(String.format("%s.%s", category.getLanguagekey(), entry.getKey()));
+ }
+ }
+ }
+
+ private void loadConfig() {
+ laserOCPenaltyFactor = config.getFloat(
+ "advasslinePenaltyFactor",
+ "common.balancing",
+ 0.3f,
+ 0f,
+ 10f,
+ "Laser overclock penalty factor. This will incredibly change the game balance. Even a small step from 0.2 to 0.3 can have very significant impact. Tweak with caution!");
+ config.save();
+ }
+
+ public Configuration getConfig() {
+ return config;
+ }
+
+ public float getLaserOCPenaltyFactor() {
+ return laserOCPenaltyFactor;
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/GGConstants.java b/gigagramfab/src/main/java/net/glease/ggfab/GGConstants.java
new file mode 100644
index 0000000000..2e68abd000
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/GGConstants.java
@@ -0,0 +1,14 @@
+package net.glease.ggfab;
+
+import net.minecraft.util.EnumChatFormatting;
+
+public class GGConstants {
+
+ public static final String MODID = "ggfab";
+ public static final String RES_PATH_ITEM = MODID + ":";
+ public static final String MODNAME = "GigaGramFab";
+ public static final String VERSION = "GRADLETOKEN_VERSION";
+
+ public static final String GGMARK = EnumChatFormatting.GOLD + "GigaGram" + EnumChatFormatting.RESET + "Fab";
+ public static final String GGMARK_TOOLTIP = "Added by " + GGMARK;
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/GGItemList.java b/gigagramfab/src/main/java/net/glease/ggfab/GGItemList.java
new file mode 100644
index 0000000000..d77d3854e1
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/GGItemList.java
@@ -0,0 +1,197 @@
+package net.glease.ggfab;
+
+import static gregtech.api.enums.GT_Values.W;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import gregtech.api.interfaces.IItemContainer;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+public enum GGItemList implements IItemContainer {
+
+ LinkedInputBus,
+ AdvAssLine,
+ // region single use tool
+ ToolCast_MV,
+ ToolCast_HV,
+ ToolCast_EV,
+ // order matters, do not insert randomly like a n00b
+ One_Use_craftingToolFile,
+ One_Use_craftingToolWrench,
+ One_Use_craftingToolCrowbar,
+ One_Use_craftingToolWireCutter,
+ One_Use_craftingToolHardHammer,
+ One_Use_craftingToolSoftHammer,
+ One_Use_craftingToolScrewdriver,
+ Shape_One_Use_craftingToolFile,
+ Shape_One_Use_craftingToolWrench,
+ Shape_One_Use_craftingToolCrowbar,
+ Shape_One_Use_craftingToolWireCutter,
+ Shape_One_Use_craftingToolHardHammer,
+ Shape_One_Use_craftingToolSoftHammer,
+ Shape_One_Use_craftingToolScrewdriver,
+ // ordered section ends
+ // endregion
+ //
+ ;
+
+ private ItemStack mStack;
+ private boolean mHasNotBeenSet = true;
+
+ @Override
+ public IItemContainer set(Item aItem) {
+ mHasNotBeenSet = false;
+ if (aItem == null) {
+ return this;
+ }
+ ItemStack aStack = new ItemStack(aItem, 1, 0);
+ mStack = GT_Utility.copyAmount(1, aStack);
+ return this;
+ }
+
+ @Override
+ public IItemContainer set(ItemStack aStack) {
+ mHasNotBeenSet = false;
+ mStack = GT_Utility.copyAmount(1, aStack);
+ return this;
+ }
+
+ @Override
+ public Item getItem() {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return null;
+ }
+ return mStack.getItem();
+ }
+
+ @Override
+ public Block getBlock() {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ return GT_Utility.getBlockFromStack(new ItemStack(getItem()));
+ }
+
+ @Override
+ public final boolean hasBeenSet() {
+ return !mHasNotBeenSet;
+ }
+
+ @Override
+ public boolean isStackEqual(Object aStack) {
+ return isStackEqual(aStack, false, false);
+ }
+
+ @Override
+ public boolean isStackEqual(Object aStack, boolean aWildcard, boolean aIgnoreNBT) {
+ if (GT_Utility.isStackInvalid(aStack)) {
+ return false;
+ }
+ return GT_Utility.areUnificationsEqual((ItemStack) aStack, aWildcard ? getWildcard(1) : get(1), aIgnoreNBT);
+ }
+
+ @Override
+ public ItemStack get(long aAmount, Object... aReplacements) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return GT_Utility.copyAmount(aAmount, aReplacements);
+ }
+ return GT_Utility.copyAmount(aAmount, GT_OreDictUnificator.get(mStack));
+ }
+
+ @Override
+ public ItemStack getWildcard(long aAmount, Object... aReplacements) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return GT_Utility.copyAmount(aAmount, aReplacements);
+ }
+ return GT_Utility.copyAmountAndMetaData(aAmount, W, GT_OreDictUnificator.get(mStack));
+ }
+
+ @Override
+ public ItemStack getUndamaged(long aAmount, Object... aReplacements) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return GT_Utility.copyAmount(aAmount, aReplacements);
+ }
+ return GT_Utility.copyAmountAndMetaData(aAmount, 0, GT_OreDictUnificator.get(mStack));
+ }
+
+ @Override
+ public ItemStack getAlmostBroken(long aAmount, Object... aReplacements) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return GT_Utility.copyAmount(aAmount, aReplacements);
+ }
+ return GT_Utility.copyAmountAndMetaData(aAmount, mStack.getMaxDamage() - 1, GT_OreDictUnificator.get(mStack));
+ }
+
+ @Override
+ public ItemStack getWithName(long aAmount, String aDisplayName, Object... aReplacements) {
+ ItemStack rStack = get(1, aReplacements);
+ if (GT_Utility.isStackInvalid(rStack)) {
+ return null;
+ }
+ rStack.setStackDisplayName(aDisplayName);
+ return GT_Utility.copyAmount(aAmount, rStack);
+ }
+
+ @Override
+ public ItemStack getWithCharge(long aAmount, int aEnergy, Object... aReplacements) {
+ ItemStack rStack = get(1, aReplacements);
+ if (GT_Utility.isStackInvalid(rStack)) {
+ return null;
+ }
+ GT_ModHandler.chargeElectricItem(rStack, aEnergy, Integer.MAX_VALUE, true, false);
+ return GT_Utility.copyAmount(aAmount, rStack);
+ }
+
+ @Override
+ public ItemStack getWithDamage(long aAmount, long aMetaValue, Object... aReplacements) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ if (GT_Utility.isStackInvalid(mStack)) {
+ return GT_Utility.copyAmount(aAmount, aReplacements);
+ }
+ return GT_Utility.copyAmountAndMetaData(aAmount, aMetaValue, GT_OreDictUnificator.get(mStack));
+ }
+
+ @Override
+ public IItemContainer registerOre(Object... aOreNames) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ for (Object tOreName : aOreNames) {
+ GT_OreDictUnificator.registerOre(tOreName, get(1));
+ }
+ return this;
+ }
+
+ @Override
+ public IItemContainer registerWildcardAsOre(Object... aOreNames) {
+ if (mHasNotBeenSet) {
+ throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!");
+ }
+ for (Object tOreName : aOreNames) {
+ GT_OreDictUnificator.registerOre(tOreName, getWildcard(1));
+ }
+ return this;
+ }
+
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/GigaGramFab.java b/gigagramfab/src/main/java/net/glease/ggfab/GigaGramFab.java
new file mode 100644
index 0000000000..52fd51a13a
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/GigaGramFab.java
@@ -0,0 +1,169 @@
+package net.glease.ggfab;
+
+import static gregtech.api.enums.ToolDictNames.*;
+import static gregtech.common.items.GT_MetaGenerated_Tool_01.*;
+import static net.glease.ggfab.api.GGFabRecipeMaps.toolCastRecipes;
+
+import net.glease.ggfab.api.GigaGramFabAPI;
+import net.glease.ggfab.items.GGMetaItem_DumbItems;
+import net.glease.ggfab.mte.MTE_AdvAssLine;
+import net.glease.ggfab.mte.MTE_LinkedInputBus;
+import net.glease.ggfab.util.GGUtils;
+import net.minecraft.item.ItemStack;
+
+import cpw.mods.fml.common.Mod;
+import cpw.mods.fml.common.event.FMLInitializationEvent;
+import cpw.mods.fml.common.event.FMLPostInitializationEvent;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine_GT_Recipe;
+import gregtech.api.util.GT_ProcessingArray_Manager;
+
+@Mod(
+ modid = GGConstants.MODID,
+ version = GGConstants.VERSION,
+ name = GGConstants.MODNAME,
+ acceptedMinecraftVersions = "[1.7.10]",
+ dependencies = "required-after:IC2;required-before:gregtech")
+public class GigaGramFab {
+
+ public GigaGramFab() {
+ // initialize the textures
+ // noinspection ResultOfMethodCallIgnored
+ BlockIcons.OVERLAY_FRONT_ADV_ASSLINE.name();
+ }
+
+ @Mod.EventHandler
+ public void preInit(FMLPreInitializationEvent event) {
+ GregTech_API.sAfterGTPreload.add(() -> {
+ GGItemList.AdvAssLine.set(
+ new MTE_AdvAssLine(13532, "ggfab.machine.adv_assline", "Advanced Assembly Line").getStackForm(1));
+ GGItemList.LinkedInputBus.set(
+ new MTE_LinkedInputBus(13533, "ggfab.machine.linked_input_bus", "Linked Input Bus", 5)
+ .getStackForm(1));
+ GGItemList.ToolCast_MV.set(
+ new GT_MetaTileEntity_BasicMachine_GT_Recipe(
+ 13534,
+ "ggfab.toolcast.tier.mv",
+ "Basic Tool Casting Machine",
+ 2,
+ "Cheap Crafting Tool for you!",
+ toolCastRecipes,
+ 1,
+ 4,
+ 32000,
+ SoundResource.NONE,
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.SpecialEffects.MAIN_RANDOM_SPARKS,
+ "TOOL_CAST",
+ new Object[] { "PGP", "WMW", "CBC", 'M', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.HULL,
+ 'P', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.PUMP, 'C',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.CIRCUIT, 'W',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.WIRE, 'G',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.GLASS, 'B',
+ ItemList.Shape_Empty.get(1L) }).getStackForm(1L));
+ GGItemList.ToolCast_HV.set(
+ new GT_MetaTileEntity_BasicMachine_GT_Recipe(
+ 13535,
+ "ggfab.toolcast.tier.hv",
+ "Advanced Tool Casting Machine",
+ 3,
+ "Cheap Crafting Tool for you!",
+ toolCastRecipes,
+ 1,
+ 4,
+ 64000,
+ SoundResource.NONE,
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.SpecialEffects.MAIN_RANDOM_SPARKS,
+ "TOOL_CAST",
+ new Object[] { "PGP", "WMW", "CBC", 'M', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.HULL,
+ 'P', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.PUMP, 'C',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.CIRCUIT, 'W',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.WIRE, 'G',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.GLASS, 'B',
+ ItemList.Shape_Empty.get(1L) }).getStackForm(1L));
+ GGItemList.ToolCast_EV.set(
+ new GT_MetaTileEntity_BasicMachine_GT_Recipe(
+ 13536,
+ "ggfab.toolcast.tier.ev",
+ "Master Tool Casting Machine",
+ 4,
+ "Cheap Crafting Tool for you!",
+ toolCastRecipes,
+ 1,
+ 4,
+ 128000,
+ SoundResource.NONE,
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.SpecialEffects.MAIN_RANDOM_SPARKS,
+ "TOOL_CAST",
+ new Object[] { "PGP", "WMW", "CBC", 'M', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.HULL,
+ 'P', GT_MetaTileEntity_BasicMachine_GT_Recipe.X.PUMP, 'C',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.CIRCUIT, 'W',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.WIRE, 'G',
+ GT_MetaTileEntity_BasicMachine_GT_Recipe.X.GLASS, 'B',
+ ItemList.Shape_Empty.get(1L) }).getStackForm(1L));
+ long plate = OrePrefixes.plate.mMaterialAmount, ingot = OrePrefixes.ingot.mMaterialAmount,
+ screw = OrePrefixes.screw.mMaterialAmount, rod = OrePrefixes.stick.mMaterialAmount;
+ GigaGramFabAPI.addSingleUseToolType(craftingToolFile, INSTANCE.mToolStats.get(FILE), 2 * plate);
+ GigaGramFabAPI.addSingleUseToolType(craftingToolWrench, INSTANCE.mToolStats.get(WRENCH), 6 * ingot);
+ GigaGramFabAPI.addSingleUseToolType(craftingToolCrowbar, INSTANCE.mToolStats.get(CROWBAR), 3 * rod);
+ GigaGramFabAPI.addSingleUseToolType(
+ craftingToolWireCutter,
+ INSTANCE.mToolStats.get(WIRECUTTER),
+ 3 * plate + 2 * rod + screw);
+ GigaGramFabAPI.addSingleUseToolType(craftingToolHardHammer, INSTANCE.mToolStats.get(HARDHAMMER), 6 * ingot);
+ GigaGramFabAPI.addSingleUseToolType(craftingToolSoftHammer, INSTANCE.mToolStats.get(SOFTMALLET), 6 * ingot);
+ GigaGramFabAPI.addSingleUseToolType(craftingToolScrewdriver, INSTANCE.mToolStats.get(SCREWDRIVER), 2 * rod);
+ GT_ProcessingArray_Manager.addRecipeMapToPA("ggfab.toolcast", toolCastRecipes);
+ });
+ GregTech_API.sBeforeGTPostload.add(new ComponentRecipeLoader());
+ GregTech_API.sBeforeGTPostload.add(new SingleUseToolRecipeLoader());
+ ConfigurationHandler.INSTANCE.init(event.getSuggestedConfigurationFile());
+
+ initDumbItem1();
+ }
+
+ @Mod.EventHandler
+ public void init(FMLInitializationEvent event) {}
+
+ @Mod.EventHandler
+ public void postInit(FMLPostInitializationEvent event) {}
+
+ private void initDumbItem1() {
+ GGMetaItem_DumbItems i1 = new GGMetaItem_DumbItems("ggfab.d1");
+ int id = 0;
+ {
+ int idShape = 30;
+ final int budget = idShape;
+ String prefix = "One_Use_craftingTool";
+ String prefix2 = "Shape_One_Use_craftingTool";
+ for (GGItemList i : GGItemList.values()) {
+ ItemStack stack = null;
+ if (i.name().startsWith(prefix)) {
+ stack = i1.addItem(
+ id++,
+ "Single Use "
+ + GGUtils.processSentence(i.name().substring(prefix.length()), ' ', true, true),
+ null,
+ i,
+ i.name().substring("One_Use_".length()));
+ } else if (i.name().startsWith(prefix2)) {
+ stack = i1.addItem(
+ idShape++,
+ "Tool Casting Mold ("
+ + GGUtils.processSentence(i.name().substring(prefix2.length()), ' ', true, true)
+ + ")",
+ null,
+ i);
+ }
+ if (stack != null) {
+ i.set(stack);
+ }
+ }
+ if (id >= budget || idShape >= 2 * budget || idShape - id != budget) throw new AssertionError();
+ id = budget * 2;
+ }
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/SingleUseToolRecipeLoader.java b/gigagramfab/src/main/java/net/glease/ggfab/SingleUseToolRecipeLoader.java
new file mode 100644
index 0000000000..8fc78b7486
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/SingleUseToolRecipeLoader.java
@@ -0,0 +1,99 @@
+package net.glease.ggfab;
+
+import static gregtech.api.enums.ToolDictNames.*;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+
+import net.glease.ggfab.api.GGFabRecipeMaps;
+import net.glease.ggfab.api.GigaGramFabAPI;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.TierEU;
+import gregtech.api.enums.ToolDictNames;
+import gregtech.api.interfaces.IToolStats;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+
+class SingleUseToolRecipeLoader implements Runnable {
+
+ @Override
+ public void run() {
+ ToolDictNames[] hardTools = new ToolDictNames[] { craftingToolHardHammer, craftingToolScrewdriver,
+ craftingToolWrench, craftingToolCrowbar, craftingToolWireCutter, craftingToolFile };
+ ToolDictNames[] softTools = new ToolDictNames[] { craftingToolSoftHammer };
+ addSingleUseToolRecipe(Materials.Steel, hardTools);
+ addSingleUseToolRecipe(Materials.Silver, 5000, hardTools);
+ addSingleUseToolRecipe(Materials.VanadiumSteel, hardTools);
+ addSingleUseToolRecipe(Materials.TungstenSteel, hardTools);
+ addSingleUseToolRecipe(Materials.HSSG, hardTools);
+ addSingleUseToolRecipe(Materials.Rubber, softTools);
+ addSingleUseToolRecipe(Materials.StyreneButadieneRubber, softTools);
+ addSingleUseToolRecipe(Materials.Polybenzimidazole, softTools);
+
+ String prefix = "Shape_One_Use_";
+ for (GGItemList value : GGItemList.values()) {
+ if (!value.name().startsWith(prefix)) {
+ continue;
+ }
+ ToolDictNames type = ToolDictNames.valueOf(value.name().substring(prefix.length()));
+ GT_ModHandler.addCraftingRecipe(
+ value.get(1L),
+ new Object[] { "h", "P", "I", 'P', ItemList.Shape_Empty, 'I', type });
+ }
+ }
+
+ private void addSingleUseToolRecipe(Materials material, ToolDictNames... types) {
+ addSingleUseToolRecipe(material, 10000, types);
+ }
+
+ private static long findNiceFactor(long fluids, long count) {
+ long end = Math.min(fluids, count);
+ for (long i = count / 256; i < end; i++) {
+ if (fluids % i == 0 && count % i == 0 && count / i < 256) return i;
+ }
+ return -1;
+ }
+
+ private void addSingleUseToolRecipe(Materials material, int outputModifier, ToolDictNames... types) {
+ if (material.mStandardMoltenFluid == null) {
+ throw new IllegalArgumentException("material does not have molten fluid form");
+ }
+ for (ToolDictNames type : types) {
+ IToolStats stats = GigaGramFabAPI.SINGLE_USE_TOOLS.get(type);
+ Long cost = GigaGramFabAPI.COST_SINGLE_USE_TOOLS.get(type);
+ if (stats == null || cost == null) {
+ throw new IllegalArgumentException(type + " not registered");
+ }
+ long fluids = cost * GT_Values.L / GT_Values.M, duration = 6 * SECONDS;
+ long count = (long) (material.mDurability * stats.getMaxDurabilityMultiplier()
+ * outputModifier
+ * 100
+ / stats.getToolDamagePerContainerCraft()
+ / 10000);
+ if (count > 64 * 4) {
+ long niceFactor = findNiceFactor(fluids, count);
+ if (niceFactor < 0) {
+ double mod = (double) count / (64 * 4L);
+ fluids = Math.max((long) (fluids / mod), 1L);
+ duration = Math.max((long) (duration / mod), 1L);
+ count = 64 * 4;
+ } else {
+ fluids /= niceFactor;
+ duration = Math.max(duration / niceFactor, 1);
+ count /= niceFactor;
+ }
+ } else if (count < 128) {
+ long mod = GT_Utility.ceilDiv(128, count);
+ fluids *= mod;
+ duration *= mod;
+ count *= mod;
+ }
+ GT_Values.RA.stdBuilder().fluidInputs(material.getMolten(fluids)) //
+ .metadata(GGFabRecipeMaps.OUTPUT_TYPE, type) //
+ .metadata(GGFabRecipeMaps.OUTPUT_COUNT, (int) count) //
+ .eut(TierEU.RECIPE_MV).duration(duration) //
+ .addTo(GGFabRecipeMaps.toolCastRecipes);
+ }
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/api/GGFabRecipeMaps.java b/gigagramfab/src/main/java/net/glease/ggfab/api/GGFabRecipeMaps.java
new file mode 100644
index 0000000000..088e9a7782
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/api/GGFabRecipeMaps.java
@@ -0,0 +1,55 @@
+package net.glease.ggfab.api;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import net.glease.ggfab.GGItemList;
+import net.minecraft.item.ItemStack;
+
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+
+import gregtech.api.enums.ToolDictNames;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMapBackend;
+import gregtech.api.recipe.RecipeMapBuilder;
+import gregtech.api.recipe.RecipeMetadataKey;
+import gregtech.api.recipe.metadata.SimpleRecipeMetadataKey;
+import gregtech.api.util.GT_Recipe;
+
+public class GGFabRecipeMaps {
+
+ public static final RecipeMetadataKey<ToolDictNames> OUTPUT_TYPE = SimpleRecipeMetadataKey
+ .create(ToolDictNames.class, "output_type");
+ public static final RecipeMetadataKey<Integer> OUTPUT_COUNT = SimpleRecipeMetadataKey
+ .create(Integer.class, "output_count");
+ public static final RecipeMap<RecipeMapBackend> toolCastRecipes = RecipeMapBuilder.of("ggfab.recipe.toolcast")
+ .maxIO(1, 4, 1, 0).minInputs(1, 1).progressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .recipeEmitter(b -> {
+ Optional<GT_Recipe> rr = b.noOptimize().validateNoInput().validateInputFluidCount(0, 1)
+ .validateNoOutput().validateNoOutputFluid().build();
+ if (!rr.isPresent()) return Collections.emptyList();
+ ToolDictNames outputType = b.getMetadata(OUTPUT_TYPE);
+ GT_Recipe r = rr.get();
+ int outputSize = b.getMetadataOrDefault(OUTPUT_COUNT, 0);
+ if (outputSize > 64 * 4 || outputSize <= 0) return Collections.emptyList();
+ ItemStack shape, output;
+ try {
+ shape = GGItemList.valueOf("Shape_One_Use_" + outputType).get(0L);
+ output = GGItemList.valueOf("One_Use_" + outputType).get(outputSize);
+ } catch (IllegalArgumentException ex) {
+ // this looks like python not java, but I don't have better way around this
+ return Collections.emptyList();
+ }
+ output.stackSize = outputSize;
+ List<ItemStack> outputs = new ArrayList<>();
+ int maxStackSize = output.getMaxStackSize();
+ while (output.stackSize > maxStackSize) outputs.add(output.splitStack(maxStackSize));
+ outputs.add(output);
+ r.mInputs = new ItemStack[] { shape };
+ r.mOutputs = outputs.toArray(new ItemStack[0]);
+ return Collections.singletonList(r);
+ }).build();
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/api/GigaGramFabAPI.java b/gigagramfab/src/main/java/net/glease/ggfab/api/GigaGramFabAPI.java
new file mode 100644
index 0000000000..7797d037c3
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/api/GigaGramFabAPI.java
@@ -0,0 +1,30 @@
+package net.glease.ggfab.api;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import gregtech.api.enums.ToolDictNames;
+import gregtech.api.interfaces.IToolStats;
+
+public class GigaGramFabAPI {
+
+ private static final Logger apiLogger = LogManager.getLogger("GigaGramFabAPI");
+
+ private static final Map<ToolDictNames, IToolStats> SINGLE_USE_TOOLS_STORE = new HashMap<>();
+ public static final Map<ToolDictNames, IToolStats> SINGLE_USE_TOOLS = Collections
+ .unmodifiableMap(SINGLE_USE_TOOLS_STORE);
+
+ private static final Map<ToolDictNames, Long> COST_SINGLE_USE_TOOLS_STORE = new HashMap<>();
+ public static final Map<ToolDictNames, Long> COST_SINGLE_USE_TOOLS = Collections
+ .unmodifiableMap(COST_SINGLE_USE_TOOLS_STORE);
+
+ public static void addSingleUseToolType(ToolDictNames type, IToolStats stat, long materialCost) {
+ if (SINGLE_USE_TOOLS_STORE.put(type, stat) != null)
+ apiLogger.warn("Replacing stat of single use tool {}", type);
+ COST_SINGLE_USE_TOOLS_STORE.put(type, materialCost);
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/items/GGMetaItem_DumbItems.java b/gigagramfab/src/main/java/net/glease/ggfab/items/GGMetaItem_DumbItems.java
new file mode 100644
index 0000000000..20a81a5abb
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/items/GGMetaItem_DumbItems.java
@@ -0,0 +1,153 @@
+package net.glease.ggfab.items;
+
+import static gregtech.api.enums.GT_Values.D1;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+import net.glease.ggfab.GGConstants;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gnu.trove.map.TIntObjectMap;
+import gnu.trove.map.hash.TIntObjectHashMap;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SubTag;
+import gregtech.api.enums.TC_Aspects;
+import gregtech.api.interfaces.IItemBehaviour;
+import gregtech.api.interfaces.IItemContainer;
+import gregtech.api.items.GT_MetaBase_Item;
+import gregtech.api.objects.ItemData;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+// mostly stolen from gt5 itself.
+public class GGMetaItem_DumbItems extends GT_MetaBase_Item {
+
+ public static final int MAX_ID = 32766;
+ private final BitSet mEnabledItems = new BitSet();
+ private final BitSet mVisibleItems = new BitSet();
+ private final ArrayList<IIcon> mIconList = new ArrayList<>();
+ private final TIntObjectMap<IItemContainer> mIconOverride = new TIntObjectHashMap<>();
+
+ public GGMetaItem_DumbItems(String aUnlocalized) {
+ super(aUnlocalized);
+ }
+
+ /**
+ * This adds a Custom Item to the ending Range.
+ *
+ * @param aID The Id of the assigned Item [0 - mItemAmount] (The MetaData gets auto-shifted by +mOffset)
+ * @param aEnglish The Default Localized Name of the created Item
+ * @param aToolTip The Default ToolTip of the created Item, you can also insert null for having no ToolTip
+ * @param aRandomData The OreDict Names you want to give the Item. Also used for TC Aspects and some other things.
+ * @return An ItemStack containing the newly created Item.
+ */
+ public final ItemStack addItem(int aID, String aEnglish, String aToolTip, Object... aRandomData) {
+ if (aID < 0 || aID > MAX_ID) return null;
+
+ if (aToolTip == null) aToolTip = "";
+ ItemStack rStack = new ItemStack(this, 1, aID);
+ mEnabledItems.set(aID);
+ mVisibleItems.set(aID);
+ GT_LanguageManager.addStringLocalization(getUnlocalizedName(rStack) + ".name", aEnglish);
+ GT_LanguageManager.addStringLocalization(getUnlocalizedName(rStack) + ".tooltip", aToolTip);
+ List<TC_Aspects.TC_AspectStack> tAspects = new ArrayList<>();
+ // Important Stuff to do first
+ for (Object tRandomData : aRandomData) if (tRandomData instanceof SubTag) {
+ if (tRandomData == SubTag.INVISIBLE) {
+ mVisibleItems.set(aID, false);
+ continue;
+ }
+ if (tRandomData == SubTag.NO_UNIFICATION) {
+ GT_OreDictUnificator.addToBlacklist(rStack);
+ }
+ }
+ // now check for the rest
+ for (Object tRandomData : aRandomData) if (tRandomData != null) {
+ boolean tUseOreDict = true;
+ if (tRandomData instanceof IItemBehaviour) {
+ @SuppressWarnings("unchecked")
+ IItemBehaviour<GT_MetaBase_Item> behavior = (IItemBehaviour<GT_MetaBase_Item>) tRandomData;
+ addItemBehavior(aID, behavior);
+ tUseOreDict = false;
+ }
+ if (tRandomData instanceof IItemContainer) {
+ ((IItemContainer) tRandomData).set(rStack);
+ tUseOreDict = false;
+ }
+ if (tRandomData instanceof SubTag) {
+ continue;
+ }
+ if (tRandomData instanceof IItemContainer) {
+ mIconOverride.put(aID, (IItemContainer) tRandomData);
+ } else if (tRandomData instanceof TC_Aspects.TC_AspectStack) {
+ ((TC_Aspects.TC_AspectStack) tRandomData).addToAspectList(tAspects);
+ } else if (tRandomData instanceof ItemData) {
+ if (GT_Utility.isStringValid(tRandomData)) {
+ GT_OreDictUnificator.registerOre(tRandomData, rStack);
+ } else {
+ GT_OreDictUnificator.addItemData(rStack, (ItemData) tRandomData);
+ }
+ } else if (tUseOreDict) {
+ GT_OreDictUnificator.registerOre(tRandomData, rStack);
+ }
+ }
+ if (GregTech_API.sThaumcraftCompat != null)
+ GregTech_API.sThaumcraftCompat.registerThaumcraftAspectsToItem(rStack, tAspects, false);
+ return rStack;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public final void registerIcons(IIconRegister aIconRegister) {
+ short j = (short) mEnabledItems.length();
+ mIconList.clear();
+ mIconList.ensureCapacity(j);
+ for (short i = 0; i < j; i++) {
+ if (mEnabledItems.get(i)) {
+ mIconList.add(aIconRegister.registerIcon(GGConstants.RES_PATH_ITEM + getUnlocalizedName() + "/" + i));
+ } else {
+ mIconList.add(null);
+ }
+ }
+ }
+
+ @Override
+ public IIcon getIconFromDamage(int aMetaData) {
+ if (aMetaData < 0 || aMetaData >= mIconList.size() || mIconList.get(aMetaData) == null)
+ return super.getIconFromDamage(aMetaData);
+ return mIconList.get(aMetaData);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ @SideOnly(Side.CLIENT)
+ public void getSubItems(Item aItem, CreativeTabs aCreativeTab, List aList) {
+ int j = mEnabledItems.length();
+ for (int i = 0; i < j; i++) {
+ if (mVisibleItems.get(i) || (D1 && mEnabledItems.get(i))) {
+ ItemStack tStack = new ItemStack(this, 1, i);
+ isItemStackUsable(tStack);
+ aList.add(tStack);
+ }
+ }
+ }
+
+ @Override
+ public Long[] getElectricStats(ItemStack aStack) {
+ return null;
+ }
+
+ @Override
+ public Long[] getFluidContainerStats(ItemStack aStack) {
+ return null;
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java b/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java
new file mode 100644
index 0000000000..de2071329d
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java
@@ -0,0 +1,1109 @@
+package net.glease.ggfab.mte;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.ExoticEnergy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.casingTexturePages;
+import static gregtech.api.util.GT_StructureUtility.buildHatchAdder;
+import static gregtech.api.util.GT_StructureUtility.ofHatchAdder;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE_ACTIVE;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE_GLOW;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE_STUCK;
+import static net.glease.ggfab.BlockIcons.OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.IntStream;
+
+import net.glease.ggfab.ConfigurationHandler;
+import net.glease.ggfab.GGConstants;
+import net.glease.ggfab.mui.ClickableTextWidget;
+import net.glease.ggfab.util.OverclockHelper;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagInt;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.StringUtils;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.widget.ISyncedWidget;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_DataAccess;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_MultiInput;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMaps;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_AssemblyLineUtils;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.GT_Waila;
+import gregtech.api.util.IGT_HatchAdder;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_InputBus_ME;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Input_ME;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/*
+ * Dev note: 1. This multi will be an assline but with greater throughput. it will take one input every 2.
+ */
+public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase<MTE_AdvAssLine>
+ implements ISurvivalConstructable {
+
+ private static final ItemStack NOT_CHECKED = new ItemStack(Blocks.dirt);
+ private static final String STRUCTURE_PIECE_FIRST = "first";
+ private static final String STRUCTURE_PIECE_LATER = "later";
+ private static final String STRUCTURE_PIECE_LAST = "last";
+ public static final String TAG_KEY_CURRENT_STICK = "mCurrentStick";
+ public static final String TAG_KEY_PROGRESS_TIMES = "mProgressTimeArray";
+ private static final IStructureDefinition<MTE_AdvAssLine> STRUCTURE_DEFINITION = StructureDefinition
+ .<MTE_AdvAssLine>builder()
+ // @formatter:off
+ .addShape(
+ STRUCTURE_PIECE_FIRST,
+ transpose(new String[][] {
+ { " ", "e", " " },
+ { "~", "l", "G" },
+ { "g", "m", "g" },
+ { "b", "i", "b" },
+ }))
+ .addShape(
+ STRUCTURE_PIECE_LATER,
+ transpose(new String[][] {
+ { " ", "e", " " },
+ { "d", "l", "d" },
+ { "g", "m", "g" },
+ { "b", "I", "b" },
+ }))
+ .addShape(
+ STRUCTURE_PIECE_LAST,
+ transpose(new String[][] {
+ { " ", "e", " " },
+ { "d", "l", "d" },
+ { "g", "m", "g" },
+ { "o", "i", "b" },
+ }))
+ // @formatter:on
+ .addElement('G', ofBlock(GregTech_API.sBlockCasings3, 10)) // grate machine casing
+ .addElement('l', ofBlock(GregTech_API.sBlockCasings2, 9)) // assembler machine casing
+ .addElement('m', ofBlock(GregTech_API.sBlockCasings2, 5)) // assembling line casing
+ .addElement(
+ 'g',
+ ofChain(
+ ofBlockUnlocalizedName("IC2", "blockAlloyGlass", 0, true),
+ ofBlockUnlocalizedName("bartworks", "BW_GlasBlocks", 0, true),
+ // warded glass
+ ofBlockUnlocalizedName("Thaumcraft", "blockCosmeticOpaque", 2, false)))
+ .addElement(
+ 'e',
+ ofChain(
+ Energy.or(ExoticEnergy)
+ .newAny(16, 1, ForgeDirection.UP, ForgeDirection.NORTH, ForgeDirection.SOUTH),
+ ofBlock(GregTech_API.sBlockCasings2, 0)))
+ .addElement(
+ 'd',
+ buildHatchAdder(MTE_AdvAssLine.class).atLeast(DataHatchElement.DataAccess).dot(2).casingIndex(42)
+ .allowOnly(ForgeDirection.NORTH).buildAndChain(GregTech_API.sBlockCasings3, 10))
+ .addElement(
+ 'b',
+ buildHatchAdder(MTE_AdvAssLine.class)
+ .atLeast(InputHatch, InputHatch, InputHatch, InputHatch, Maintenance).casingIndex(16).dot(3)
+ .allowOnly(ForgeDirection.DOWN).buildAndChain(
+ ofBlock(GregTech_API.sBlockCasings2, 0),
+ ofHatchAdder(MTE_AdvAssLine::addOutputToMachineList, 16, 4)))
+ .addElement(
+ 'I',
+ ofChain(
+ // all blocks nearby use solid steel casing, so let's use the texture of that
+ InputBus.newAny(16, 5, ForgeDirection.DOWN),
+ ofHatchAdder(MTE_AdvAssLine::addOutputToMachineList, 16, 4)))
+ .addElement('i', InputBus.newAny(16, 5, ForgeDirection.DOWN))
+ .addElement('o', OutputBus.newAny(16, 4, ForgeDirection.DOWN)).build();
+ private ItemStack currentStick;
+ private GT_Recipe.GT_Recipe_AssemblyLine currentRecipe;
+ private final Slice[] slices = IntStream.range(0, 16).mapToObj(Slice::new).toArray(Slice[]::new);
+ private boolean processing;
+ private long inputVoltage;
+ // surely no one is using more EUt than this, no?
+ private long inputEUt;
+ private long baseEUt;
+ private boolean stuck;
+
+ private final List<GT_MetaTileEntity_Hatch_DataAccess> mDataAccessHatches = new ArrayList<>();
+ private Map<GT_Utility.ItemId, ItemStack> curBatchItemsFromME;
+ private Map<Fluid, FluidStack> curBatchFluidsFromME;
+ private int currentInputLength;
+ private String lastStopReason = "";
+ private int currentRecipeParallel = 1;
+ // Batch mode will increase parallel per slice to try to get as close as possible to this amount of ticks
+ // per slice, but will never go over this amount.
+ private static final int BATCH_MODE_DESIRED_TICKS_PER_SLICE = 128;
+
+ public MTE_AdvAssLine(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public MTE_AdvAssLine(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new MTE_AdvAssLine(mName);
+ }
+
+ public boolean addDataAccessToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_DataAccess) {
+ ((GT_MetaTileEntity_Hatch) aMetaTileEntity).updateTexture(aBaseCasingIndex);
+ return mDataAccessHatches.add((GT_MetaTileEntity_Hatch_DataAccess) aMetaTileEntity);
+ }
+ return false;
+ }
+
+ private boolean checkMachine() {
+ return checkMachine(true) || checkMachine(false);
+ }
+
+ private boolean checkMachine(boolean leftToRight) {
+ clearHatches();
+ if (!checkPiece(STRUCTURE_PIECE_FIRST, 0, 1, 0)) return false;
+ for (int i = 1; i < 16; i++) {
+ if (!checkPiece(STRUCTURE_PIECE_LATER, leftToRight ? -i : i, 1, 0)) return false;
+ if (!mOutputBusses.isEmpty())
+ return (!mEnergyHatches.isEmpty() || !mExoticEnergyHatches.isEmpty()) && mMaintenanceHatches.size() == 1
+ && mDataAccessHatches.size() <= 1;
+ }
+ return false;
+ }
+
+ @Override
+ public void construct(ItemStack stackSize, boolean hintsOnly) {
+ buildPiece(STRUCTURE_PIECE_FIRST, stackSize, hintsOnly, 0, 1, 0);
+ int tLength = Math.min(stackSize.stackSize + 3, 16); // render 4 slices at minimal
+ for (int i = 1; i < tLength; i++) {
+ buildPiece(STRUCTURE_PIECE_LATER, stackSize, hintsOnly, -i, 1, 0);
+ }
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ int build = survivialBuildPiece(STRUCTURE_PIECE_FIRST, stackSize, 0, 1, 0, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ int tLength = Math.min(stackSize.stackSize + 3, 16); // render 4 slices at minimal
+ for (int i = 1; i < tLength - 1; i++) {
+ build = survivialBuildPiece(STRUCTURE_PIECE_LATER, stackSize, -i, 1, 0, elementBudget, env, false, true);
+ if (build >= 0) return build;
+ }
+ return survivialBuildPiece(STRUCTURE_PIECE_LAST, stackSize, 1 - tLength, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ super.initDefaultModes(aNBT);
+ // blockrenderer6343 seems to place the block in a weird way, let's catch that
+ if (getBaseMetaTileEntity() != null && getBaseMetaTileEntity().isServerSide()) {
+ UUID ownerUuid = getBaseMetaTileEntity().getOwnerUuid();
+ if (ownerUuid == null) return;
+ float factor = ConfigurationHandler.INSTANCE.getLaserOCPenaltyFactor();
+ MinecraftServer server = MinecraftServer.getServer();
+ // more blockrenderer6343 weirdness
+ if (server == null) return;
+ @SuppressWarnings("unchecked")
+ List<EntityPlayerMP> l = server.getConfigurationManager().playerEntityList;
+ for (EntityPlayerMP p : l) {
+ if (p.getUniqueID().equals(ownerUuid)) {
+ for (int i = 0; i < 9; i++) {
+ // switch is stupid, but I have no better idea
+ Object[] args;
+ switch (i) {
+ case 7:
+ args = new Object[] { factor };
+ break;
+ case 8:
+ args = new Object[] { (int) (factor * 100) + 400,
+ (int) ((4 + factor) * (4 + factor + factor) * 100), 4 + factor,
+ 4 + factor + factor };
+ break;
+ default:
+ args = new Object[0];
+ }
+ p.addChatMessage(new ChatComponentTranslation("ggfab.info.advassline." + i, args));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean aActive, boolean aRedstone) {
+ if (side == facing) {
+ if (stuck) {
+ return new ITexture[] { casingTexturePages[0][16],
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE_STUCK).extFacing().build(),
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW).extFacing().glow()
+ .build() };
+ }
+ if (aActive) return new ITexture[] { casingTexturePages[0][16],
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE_ACTIVE).extFacing().build(),
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW).extFacing().glow()
+ .build() };
+ return new ITexture[] { casingTexturePages[0][16],
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE).extFacing().build(),
+ TextureFactory.builder().addIcon(OVERLAY_FRONT_ADV_ASSLINE_GLOW).extFacing().glow().build() };
+ }
+ return new ITexture[] { casingTexturePages[0][16] };
+ }
+
+ @Override
+ protected GT_Multiblock_Tooltip_Builder createTooltip() {
+ final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder();
+ tt.addMachineType("Assembling Line").addInfo("Controller block for the Advanced Assembling Line")
+ .addInfo("Built exactly the same as standard Assembling Line")
+ .addInfo("Place in world to get more info. It will be a lengthy read.")
+ .addInfo("Assembling Line with item pipelining").addInfo("All fluids are however consumed at start")
+ .addInfo("Use voltage of worst energy hatch for overclocking")
+ .addInfo("EU/t is (number of slices working) * (overclocked EU/t)").addSeparator()
+ .beginVariableStructureBlock(5, 16, 4, 4, 3, 3, false)
+ .addStructureInfo("From Bottom to Top, Left to Right")
+ .addStructureInfo(
+ "Layer 1 - Solid Steel Machine Casing, Input Bus (last can be Output Bus), Solid Steel Machine Casing")
+ .addStructureInfo(
+ "Layer 2 - Borosilicate Glass(any)/Warded Glass/Reinforced Glass, Assembling Line Casing, Reinforced Glass")
+ .addStructureInfo("Layer 3 - Grate Machine Casing, Assembler Machine Casing, Grate Machine Casing")
+ .addStructureInfo("Layer 4 - Empty, Solid Steel Machine Casing, Empty")
+ .addStructureInfo("Up to 16 repeating slices, each one allows for 1 more item in recipes")
+ .addController("Either Grate on layer 3 of the first slice").addEnergyHatch("Any layer 4 casing", 1)
+ .addMaintenanceHatch("Any layer 1 casing", 3).addInputBus("As specified on layer 1", 4, 5)
+ .addInputHatch("Any layer 1 casing", 3)
+ .addOutputBus("Replaces Input Bus on final slice or on any solid steel casing on layer 1", 4)
+ .addOtherStructurePart("Data Access Hatch", "Optional, next to controller", 2)
+ .toolTipFinisher(GGConstants.GGMARK);
+ return tt;
+ }
+
+ private void setCurrentRecipe(ItemStack stick, GT_Recipe.GT_Recipe_AssemblyLine recipe) {
+ currentRecipe = recipe;
+ currentStick = stick;
+ currentInputLength = recipe.mInputs.length;
+ // Reset parallel, we need to re-check on next recipe check to see if there are enough items in the first slice
+ currentRecipeParallel = 1;
+ }
+
+ private void clearCurrentRecipe() {
+ currentRecipe = null;
+ currentStick = null;
+ currentInputLength = -1;
+ stuck = false;
+ baseEUt = 0;
+ for (Slice slice : slices) {
+ slice.reset();
+ }
+ mMaxProgresstime = 0;
+ getBaseMetaTileEntity().issueClientUpdate();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setString("lastStop", lastStopReason);
+ // we need to check for active here.
+ // if machine was turned off via soft mallet it will not call checkRecipe() on recipe end
+ // in that case we don't have a current recipe, so this should be ignored
+ if (getBaseMetaTileEntity().isActive() && GT_Utility.isStackValid(currentStick)) {
+ aNBT.setTag(TAG_KEY_CURRENT_STICK, currentStick.writeToNBT(new NBTTagCompound()));
+ aNBT.setInteger("mRecipeHash", currentRecipe.getPersistentHash());
+ aNBT.setIntArray(
+ TAG_KEY_PROGRESS_TIMES,
+ Arrays.stream(slices).limit(currentInputLength).mapToInt(s -> s.progress).toArray());
+ aNBT.setBoolean("stuck", stuck);
+ aNBT.setLong("inputV", inputVoltage);
+ aNBT.setLong("inputEU", inputEUt);
+ aNBT.setLong("baseEU", baseEUt);
+ aNBT.setInteger("currentParallel", currentRecipeParallel);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ lastStopReason = aNBT.getString("lastStop");
+ ItemStack loadedStack = null;
+ GT_Recipe.GT_Recipe_AssemblyLine recipe = null;
+ if (aNBT.hasKey(TAG_KEY_PROGRESS_TIMES, Constants.NBT.TAG_INT_ARRAY)) {
+ int[] arr = aNBT.getIntArray(TAG_KEY_PROGRESS_TIMES);
+ for (int i = 0; i < slices.length; i++) {
+ if (i < arr.length) {
+ slices[i].progress = arr[i];
+ if (arr[i] == 0)
+ // this will be synced to client by first MTE packet to client
+ stuck = true;
+ } else slices[i].reset();
+ }
+ }
+ if (aNBT.hasKey(TAG_KEY_CURRENT_STICK, Constants.NBT.TAG_COMPOUND)) {
+ loadedStack = ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag(TAG_KEY_CURRENT_STICK));
+ GT_AssemblyLineUtils.LookupResult lookupResult = GT_AssemblyLineUtils
+ .findAssemblyLineRecipeFromDataStick(loadedStack, false);
+ switch (lookupResult.getType()) {
+ case VALID_STACK_AND_VALID_HASH:
+ recipe = lookupResult.getRecipe();
+ stuck = aNBT.getBoolean("stuck");
+ inputVoltage = aNBT.getLong("inputV");
+ inputEUt = aNBT.getLong("inputEU");
+ baseEUt = aNBT.getLong("baseEU");
+ currentRecipeParallel = aNBT.getInteger("currentParallel");
+ if (inputVoltage <= 0 || inputEUt <= 0 || baseEUt >= 0) {
+ criticalStopMachine("ggfab.gui.advassline.shutdown.load.energy");
+ loadedStack = null;
+ recipe = null;
+ }
+ break;
+ case VALID_STACK_AND_VALID_RECIPE:
+ // recipe is there, but it has been changed. to prevent issues, abort the current recipe
+ // TODO finish the last recipe instead of aborting
+ default:
+ // recipe is gone. to prevent issues, abort the current recipe
+ criticalStopMachine("ggfab.gui.advassline.shutdown.load.recipe");
+ loadedStack = null;
+ break;
+ }
+ }
+ if (loadedStack == null || recipe == null) clearCurrentRecipe();
+ else setCurrentRecipe(loadedStack, recipe);
+ }
+
+ /**
+ * roughly the same as {@link #criticalStopMachine()}, but does not attempt to send a halting sound if world is not
+ * loaded. also supports setting a stop reason
+ */
+ private void criticalStopMachine(String reason) {
+ int oMaxProgresstime = mMaxProgresstime;
+ stopMachine();
+ // don't do these at all if the machine wasn't working before anyway
+ if (oMaxProgresstime > 0) {
+ if (getBaseMetaTileEntity().getWorld() != null) sendSound(INTERRUPT_SOUND_INDEX);
+ getBaseMetaTileEntity().setShutdownStatus(true);
+ lastStopReason = reason;
+ }
+ }
+
+ @Override
+ public IStructureDefinition<MTE_AdvAssLine> getStructureDefinition() {
+ return STRUCTURE_DEFINITION;
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+ mExoticEnergyHatches.clear();
+ mDataAccessHatches.clear();
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ if (checkMachine() && (mEnergyHatches.size() > 0 || mExoticEnergyHatches.size() > 0)) {
+ long oV = inputVoltage, oEut = inputEUt;
+ inputVoltage = Integer.MAX_VALUE;
+ inputEUt = 0;
+ mEnergyHatches.forEach(this::recordEnergySupplier);
+ mExoticEnergyHatches.forEach(this::recordEnergySupplier);
+ if (mMaxProgresstime > 0 && (oV != inputVoltage || oEut != inputEUt)) {
+ criticalStopMachine("ggfab.gui.advassline.shutdown.structure");
+ }
+ return true;
+ } else {
+ inputVoltage = V[0];
+ return false;
+ }
+ }
+
+ private void recordEnergySupplier(GT_MetaTileEntity_Hatch hatch) {
+ if (!hatch.isValid()) return;
+ inputEUt += hatch.maxEUInput() * hatch.maxWorkingAmperesIn();
+ inputVoltage = Math.min(inputVoltage, hatch.maxEUInput());
+ if (inputEUt < 0) inputEUt = Long.MAX_VALUE;
+ }
+
+ @Override
+ protected void startRecipeProcessing() {
+ if (!processing) {
+ super.startRecipeProcessing();
+ curBatchItemsFromME = getStoredInputsFromME();
+ curBatchFluidsFromME = getStoredFluidsFromME();
+ processing = true;
+ }
+ }
+
+ @Override
+ protected void endRecipeProcessing() {
+ if (!processing) return;
+ super.endRecipeProcessing();
+ processing = false;
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ boolean oStuck = stuck;
+ stuck = (aValue & 1) == 1;
+ if (oStuck != stuck) getBaseMetaTileEntity().issueTextureUpdate();
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return (byte) (stuck ? 1 : 0);
+ }
+
+ @Override
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ super.drawTexts(screenElements, inventorySlot);
+ /*
+ * SliceStatusWidget[] arr =
+ * Arrays.stream(slices).map(SliceStatusWidget::new).toArray(SliceStatusWidget[]::new);
+ * screenElements.widgets(arr); screenElements.widget(new FakeSyncWidget.IntegerSyncer(() -> currentInputLength,
+ * l -> { currentInputLength = l; for (SliceStatusWidget w : arr) { w.updateText(); } }));
+ */
+ screenElements.widget(
+ new TextWidget(Text.localised("ggfab.gui.advassline.shutdown"))
+ .setEnabled(this::hasAbnormalStopReason));
+ screenElements.widget(
+ new TextWidget().setTextSupplier(() -> Text.localised(lastStopReason))
+ .attachSyncer(
+ new FakeSyncWidget.StringSyncer(() -> lastStopReason, r -> this.lastStopReason = r),
+ screenElements)
+ .setEnabled(this::hasAbnormalStopReason));
+ screenElements.widget(
+ new ClickableTextWidget(
+ Text.localised("ggfab.gui.advassline.shutdown_clear").alignment(Alignment.CenterLeft))
+ .setMarginInLines(0).setOnClick((d, w) -> lastStopReason = "").setSize(36, 20)
+ .setEnabled(this::hasAbnormalStopReason));
+ }
+
+ private Boolean hasAbnormalStopReason(Widget w) {
+ return !StringUtils.isNullOrEmpty(lastStopReason);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return RecipeMaps.assemblylineVisualRecipes;
+ }
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (currentRecipe == null) {
+ criticalStopMachine("ggfab.gui.advassline.shutdown.recipe_null");
+ return false;
+ }
+ for (GT_MetaTileEntity_Hatch_DataAccess hatch_dataAccess : mDataAccessHatches) {
+ hatch_dataAccess.setActive(true);
+ }
+
+ if (mInputBusses.size() < currentInputLength) {
+ criticalStopMachine("ggfab.gui.advassline.shutdown.input_busses");
+ return false;
+ }
+ boolean oStuck = stuck;
+ stuck = false;
+
+ for (int i = slices.length - 1; i >= 0; i--) {
+ slices[i].tick();
+ }
+
+ if (oStuck != stuck)
+ // send the status as it has changed
+ getBaseMetaTileEntity().issueClientUpdate();
+
+ if (getBaseMetaTileEntity().isAllowedToWork() && slices[0].progress < 0) {
+ startRecipeProcessing();
+ if (hasAllItems(currentRecipe, this.currentRecipeParallel)
+ && hasAllFluids(currentRecipe, this.currentRecipeParallel)
+ && slices[0].start()) {
+ drainAllFluids(currentRecipe, this.currentRecipeParallel);
+ mProgresstime = 0;
+ }
+ }
+
+ boolean foundWorking = false;
+ int working = 0;
+ for (Slice slice : slices) {
+ if (slice.progress >= 0) {
+ if (!foundWorking) {
+ foundWorking = true;
+ mProgresstime = (slice.id + 1) * (mMaxProgresstime / currentInputLength) - slice.progress;
+ }
+ }
+ if (slice.progress > 0) working++;
+ }
+ lEUt = working * baseEUt;
+
+ if (lEUt > 0) {
+ // overflow again :(
+ lEUt = Long.MIN_VALUE;
+ for (int i = 0; i < working; i++) {
+ if (!drainEnergyInput(-baseEUt)) {
+ criticalStopMachine("ggfab.gui.advassline.shutdown.energy");
+ return false;
+ }
+ }
+ } else {
+ if (!super.onRunningTick(aStack)) return false;
+ }
+
+ endRecipeProcessing();
+ return true;
+ }
+
+ private ItemStack getInputBusContent(int index) {
+ if (index < 0 || index >= mInputBusses.size()) return null;
+ GT_MetaTileEntity_Hatch_InputBus inputBus = mInputBusses.get(index);
+ if (!inputBus.isValid()) return null;
+ if (inputBus instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) {
+ ItemStack item = meBus.getShadowItemStack(0);
+ if (item == null) return null;
+ GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(item);
+ if (!curBatchItemsFromME.containsKey(id)) return null;
+ return curBatchItemsFromME.get(id);
+ }
+ return inputBus.getStackInSlot(0);
+
+ }
+
+ private FluidStack getInputHatchContent(int index) {
+ if (index < 0 || index >= mInputHatches.size()) return null;
+ GT_MetaTileEntity_Hatch_Input inputHatch = mInputHatches.get(index);
+ if (!inputHatch.isValid()) return null;
+ if (inputHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ FluidStack fluid = meHatch.getShadowFluidStack(0);
+ if (fluid == null) return null;
+ if (!curBatchFluidsFromME.containsKey(fluid.getFluid())) return null;
+ return curBatchFluidsFromME.get(fluid.getFluid());
+ }
+ if (inputHatch instanceof GT_MetaTileEntity_Hatch_MultiInput multiHatch) {
+ return multiHatch.getFluid(0);
+ }
+ return inputHatch.getFillableStack();
+ }
+
+ private GT_Recipe.GT_Recipe_AssemblyLine findRecipe(ItemStack tDataStick) {
+ GT_AssemblyLineUtils.LookupResult tLookupResult = GT_AssemblyLineUtils
+ .findAssemblyLineRecipeFromDataStick(tDataStick, false);
+
+ if (tLookupResult.getType() == GT_AssemblyLineUtils.LookupResultType.INVALID_STICK) return null;
+
+ GT_Recipe.GT_Recipe_AssemblyLine tRecipe = tLookupResult.getRecipe();
+ // Check if the recipe on the data stick is the current recipe for it's given output, if not we update it
+ // and continue to next.
+ if (tLookupResult.getType() != GT_AssemblyLineUtils.LookupResultType.VALID_STACK_AND_VALID_HASH) {
+ tRecipe = GT_AssemblyLineUtils.processDataStick(tDataStick);
+ if (tRecipe == null) {
+ return null;
+ }
+ }
+
+ // So here we check against the recipe found on the data stick.
+ // If we run into missing buses/hatches or bad inputs, we go to the next data stick.
+ // This check only happens if we have a valid up-to-date data stick.
+
+ // Check item Inputs align. For this we do not need to consider batch mode parallels yet, this will be done
+ // later on during recipe start.
+ if (!hasAllItems(tRecipe, 1)) return null;
+
+ // Check Fluid Inputs align. Again, do not consider parallels
+ if (!hasAllFluids(tRecipe, 1)) return null;
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Check overclock");
+ }
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Find available recipe");
+ }
+ return tRecipe;
+ }
+
+ private boolean hasAllItems(GT_Recipe.GT_Recipe_AssemblyLine tRecipe, int parallel) {
+ int aItemCount = tRecipe.mInputs.length;
+ if (mInputBusses.size() < aItemCount) return false;
+ int[] itemConsumptions = GT_Recipe.GT_Recipe_AssemblyLine.getItemConsumptionAmountArray(mInputBusses, tRecipe);
+ if (itemConsumptions == null || itemConsumptions.length == 0) {
+ return false;
+ }
+ int maxParallel = (int) GT_Recipe.GT_Recipe_AssemblyLine
+ .maxParallelCalculatedByInputItems(mInputBusses, parallel, itemConsumptions, curBatchItemsFromME);
+ return maxParallel >= parallel;
+ }
+
+ private boolean hasAllFluids(GT_Recipe.GT_Recipe_AssemblyLine tRecipe, int parallel) {
+ int aFluidCount = tRecipe.mFluidInputs.length;
+ if (mInputHatches.size() < aFluidCount) return false;
+ int maxParallel = (int) GT_Recipe.GT_Recipe_AssemblyLine.maxParallelCalculatedByInputFluids(
+ mInputHatches,
+ parallel,
+ tRecipe.mFluidInputs,
+ curBatchFluidsFromME);
+ return maxParallel >= parallel;
+ }
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ private boolean isCorrectDataItem(ItemStack aStack, int state) {
+ if ((state & 1) != 0 && ItemList.Circuit_Integrated.isStackEqual(aStack, true, true)) return true;
+ if ((state & 2) != 0 && ItemList.Tool_DataStick.isStackEqual(aStack, false, true)) return true;
+ return (state & 4) != 0 && ItemList.Tool_DataOrb.isStackEqual(aStack, false, true);
+ }
+
+ /**
+ * @param state using bitmask, 1 for IntegratedCircuit, 2 for DataStick, 4 for DataOrb
+ */
+ public ArrayList<ItemStack> getDataItems(int state) {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+ if (GT_Utility.isStackValid(mInventory[1]) && isCorrectDataItem(mInventory[1], state)) {
+ rList.add(mInventory[1]);
+ }
+ for (GT_MetaTileEntity_Hatch_DataAccess tHatch : mDataAccessHatches) {
+ if (tHatch.isValid()) {
+ for (int i = 0; i < tHatch.getBaseMetaTileEntity().getSizeInventory(); i++) {
+ if (tHatch.getBaseMetaTileEntity().getStackInSlot(i) != null
+ && isCorrectDataItem(tHatch.getBaseMetaTileEntity().getStackInSlot(i), state))
+ rList.add(tHatch.getBaseMetaTileEntity().getStackInSlot(i));
+ }
+ }
+ }
+ return rList;
+ }
+
+ // this is only called when all slices have finished their work
+ // and the first slice cannot find a input/fluid cannot be found
+ // so we are safe to assume the old recipe no longer works
+ @Override
+ @NotNull
+ public CheckRecipeResult checkProcessing() {
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Start Adv ALine recipe check");
+ }
+ clearCurrentRecipe();
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_DATA_STICKS;
+ ArrayList<ItemStack> tDataStickList = getDataItems(2);
+ if (tDataStickList.isEmpty()) {
+ return result;
+ }
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Stick accepted, " + tDataStickList.size() + " Data Sticks found");
+ }
+
+ GT_Recipe.GT_Recipe_AssemblyLine recipe = null;
+
+ for (ItemStack stack : tDataStickList) {
+ recipe = findRecipe(stack);
+ if (recipe == null) {
+ result = CheckRecipeResultRegistry.NO_RECIPE;
+ continue;
+ }
+ if (recipe.mEUt > inputVoltage) {
+ result = CheckRecipeResultRegistry.insufficientPower(recipe.mEUt);
+ continue;
+ }
+
+ setCurrentRecipe(stack, recipe);
+ // first overclock normally
+ // we use the new oc calculator instead
+ // calculateOverclockedNessMulti from super class has a mysterious 5% cable loss thing at the moment
+ // of writing
+ GT_OverclockCalculator ocCalc = new GT_OverclockCalculator().setRecipeEUt(currentRecipe.mEUt)
+ .setDuration(Math.max(recipe.mDuration / recipe.mInputs.length, 1)).setEUt(inputVoltage)
+ .calculate();
+ // since we already checked mEUt <= inputVoltage, no need to check if recipe is too OP
+ lEUt = ocCalc.getConsumption();
+ mMaxProgresstime = ocCalc.getDuration();
+ // then laser overclock if needed
+ if (!mExoticEnergyHatches.isEmpty()) {
+ OverclockHelper.OverclockOutput laserOverclock = OverclockHelper.laserOverclock(
+ lEUt,
+ mMaxProgresstime,
+ inputEUt / recipe.mInputs.length,
+ ConfigurationHandler.INSTANCE.getLaserOCPenaltyFactor());
+ if (laserOverclock != null) {
+ lEUt = laserOverclock.getEUt();
+ mMaxProgresstime = laserOverclock.getDuration();
+ }
+ }
+ // Save this for batch mode parallel calculations
+ int timePerSlice = mMaxProgresstime;
+ // correct the recipe duration
+ mMaxProgresstime *= recipe.mInputs.length;
+
+ // Finally apply batch mode parallels if possible.
+ // For this we need to verify the first item slot and all fluids slots have enough resources
+ // to execute parallels.
+ // Note that we skip this entirely if the time for each slice is more than
+ // BATCH_MODE_DESIRED_TICKS_PER_SLICE ticks, since in this case the amount of batches will always be 1
+ if (super.isBatchModeEnabled() && timePerSlice < BATCH_MODE_DESIRED_TICKS_PER_SLICE) {
+ // Calculate parallel based on time per slice, and the amount of fluid in the first fluid slot.
+ // We use fluid, since this way players can limit parallel by controlling how much fluid
+ // ends up in each AAL. This way, batch mode will not slow down setups where multiple AAL
+ // are connected to the same set of input items. Note that this will still suffer from the same
+ // issue if using stocking hatch, but in this case increasing pattern size can help.
+
+ // Note that every assline recipe has a fluid ingredient.
+ FluidStack firstFluidSlot = getInputHatchContent(0);
+ if (firstFluidSlot == null) {
+ result = CheckRecipeResultRegistry.INTERNAL_ERROR;
+ break;
+ }
+ int recipesAvailable = Math.floorDiv(firstFluidSlot.amount, recipe.mFluidInputs[0].amount);
+ // Divide recipes available by the amount of slices in the recipe. This will prevent the AAL from
+ // batching instead of parallelizing, which would make it effectively slower.
+ recipesAvailable = Math.floorDiv(recipesAvailable, recipe.mInputs.length);
+ // Sanity check to avoid this being zero when there is only one recipe available.
+ recipesAvailable = Math.max(recipesAvailable, 1);
+ int desiredBatches = Math.floorDiv(BATCH_MODE_DESIRED_TICKS_PER_SLICE, timePerSlice);
+ // Limit the amount of parallel to both the amount of recipes available and the maximum number
+ // of batches we want to run. The latter is done to prevent batch mode from ever going above
+ // BATCH_MODE_DESIRED_TICKS_PER_SLICE ticks per slice (see also where it is defined above).
+ int parallel = Math.min(recipesAvailable, desiredBatches);
+ if (hasAllFluids(recipe, parallel) && hasAllItems(recipe, parallel)) {
+ this.currentRecipeParallel = parallel;
+ // Update recipe duration with final batch mode multiplier
+ mMaxProgresstime *= this.currentRecipeParallel;
+ }
+ }
+ result = CheckRecipeResultRegistry.SUCCESSFUL;
+ break;
+ }
+ if (!result.wasSuccessful()) {
+ clearCurrentRecipe();
+ return result;
+ }
+ if (recipe == null || !slices[0].start() || currentRecipeParallel <= 0) {
+ clearCurrentRecipe();
+ // something very very wrong...
+ return CheckRecipeResultRegistry.INTERNAL_ERROR;
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("All checked start consuming inputs");
+ }
+ drainAllFluids(recipe, this.currentRecipeParallel);
+
+ // Apply parallel
+ mOutputItems = new ItemStack[] { recipe.mOutput.copy() };
+ mOutputItems[0].stackSize *= this.currentRecipeParallel;
+
+ if (this.lEUt > 0) {
+ this.lEUt = -this.lEUt;
+ }
+ baseEUt = lEUt;
+ this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+ this.mEfficiencyIncrease = 10000;
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Recipe successful");
+ }
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return true;
+ }
+
+ @Override
+ public Set<VoidingMode> getAllowedVoidingModes() {
+ return VoidingMode.ITEM_ONLY_MODES;
+ }
+
+ @Override
+ public boolean isCorrectMachinePart(ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public int getMaxEfficiency(ItemStack aStack) {
+ return 10000;
+ }
+
+ @Override
+ public int getDamageToComponent(ItemStack aStack) {
+ return 0;
+ }
+
+ @Override
+ public boolean explodesOnComponentBreak(ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ NBTTagCompound tag = accessor.getNBTData();
+ String machineProgressString = GT_Waila.getMachineProgressString(
+ tag.getBoolean("isActive"),
+ tag.getInteger("maxProgress"),
+ tag.getInteger("progress"));
+ currentTip.remove(machineProgressString);
+
+ int duration = tag.getInteger("mDuration");
+ if (tag.hasKey(TAG_KEY_PROGRESS_TIMES, Constants.NBT.TAG_LIST)) {
+ NBTTagList tl = tag.getTagList(TAG_KEY_PROGRESS_TIMES, Constants.NBT.TAG_INT);
+ @SuppressWarnings("unchecked")
+ List<NBTTagInt> list = tl.tagList;
+ for (int i = 0, listSize = list.size(); i < listSize; i++) {
+ NBTTagInt t = list.get(i);
+ int progress = t.func_150287_d();
+ if (progress == 0) {
+ currentTip.add(I18n.format("ggfab.waila.advassline.slice.stuck", i + 1));
+ } else if (progress < 0) {
+ currentTip.add(I18n.format("ggfab.waila.advassline.slice.idle", i + 1));
+ } else if (duration > 40) {
+ currentTip.add(
+ I18n.format(
+ "ggfab.waila.advassline.slice",
+ i + 1,
+ (duration - progress) / 20,
+ duration / 20));
+ } else {
+ currentTip.add(
+ I18n.format("ggfab.waila.advassline.slice.small", i + 1, duration - progress, duration));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ if (currentRecipe == null || !getBaseMetaTileEntity().isActive()) return;
+ NBTTagList l = new NBTTagList();
+ for (int i = 0; i < currentInputLength; i++) {
+ l.appendTag(new NBTTagInt(slices[i].progress));
+ }
+ tag.setTag(TAG_KEY_PROGRESS_TIMES, l);
+ tag.setInteger("mDuration", mMaxProgresstime / currentInputLength);
+ }
+
+ /**
+ * Caller is responsible to check and ensure the hatches are there and has all the fluid needed. You will usually
+ * want to ensure hasAllFluid was called right before calling this, otherwise very bad things can happen.
+ */
+ private void drainAllFluids(GT_Recipe.GT_Recipe_AssemblyLine recipe, int parallel) {
+ GT_Recipe.GT_Recipe_AssemblyLine
+ .consumeInputFluids(mInputHatches, parallel, recipe.mFluidInputs, curBatchFluidsFromME);
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) tHatch.updateSlots();
+ }
+
+ @Override
+ public void stopMachine(@NotNull ShutDownReason reason) {
+ clearCurrentRecipe();
+ super.stopMachine(reason);
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return true;
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ if (aPlayer.isSneaking()) {
+ batchMode = !batchMode;
+ if (batchMode) {
+ GT_Utility.sendChatToPlayer(aPlayer, "Batch mode enabled");
+ } else {
+ GT_Utility.sendChatToPlayer(aPlayer, "Batch mode disabled");
+ }
+ }
+ return true;
+ }
+
+ private class SliceStatusWidget extends TextWidget implements ISyncedWidget {
+
+ private final Slice slice;
+ private int lastProgress = -2;
+ private Text text;
+
+ private SliceStatusWidget(Slice slice) {
+ this.slice = slice;
+ updateText();
+ setEnabled(w -> slice.progress == 0 && currentInputLength > slice.id);
+ }
+
+ @Override
+ public Text getText() {
+ return text;
+ }
+
+ @Override
+ public void readOnClient(int id, PacketBuffer buf) {
+ if (id == 0) {
+ slice.progress = buf.readVarIntFromBuffer();
+ updateText();
+ checkNeedsRebuild();
+ }
+ }
+
+ public void updateText() {
+ String type = "unknown";
+ if (slice.progress == 0) type = "stuck";
+ else if (slice.progress < 0) type = "idle";
+ text = Text.localised("ggfab.gui.advassline.slice." + type, slice.id);
+ }
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {}
+
+ @Override
+ public void detectAndSendChanges(boolean init) {
+ if (slice.progress != lastProgress) {
+ // suppress small normal progress update
+ if (slice.progress > 0 && lastProgress > 0 && lastProgress - slice.progress < 10) return;
+ lastProgress = slice.progress;
+ syncToClient(0, b -> b.writeVarIntToBuffer(slice.progress));
+ }
+ }
+
+ @Override
+ public void markForUpdate() {}
+
+ @Override
+ public void unMarkForUpdate() {}
+
+ @Override
+ public boolean isMarkedForUpdate() {
+ return false;
+ }
+ }
+
+ private class Slice {
+
+ private final int id;
+ private int progress = -1;
+
+ public Slice(int id) {
+ this.id = id;
+ }
+
+ public void reset() {
+ progress = -1;
+ }
+
+ public void tick() {
+ if (progress < 0) return;
+ if (progress == 0 || --progress == 0) {
+ // id==0 will be end of chain if 1 input, so we need a +1 here
+ if (id + 1 >= currentInputLength) {
+ // use previously calculated parallel output
+ ItemStack output = mOutputItems[0];
+ if (addOutput(output) || !voidingMode.protectItem) reset();
+ else stuck = true;
+ } else {
+ if (slices[id + 1].start()) reset();
+ else stuck = true;
+ }
+ }
+ }
+
+ public boolean start() {
+ if (progress >= 0) return false;
+ startRecipeProcessing();
+ ItemStack stack = getInputBusContent(id);
+ if (stack == null) return false;
+ int size = GT_Recipe.GT_Recipe_AssemblyLine
+ .getMatchedIngredientAmount(stack, currentRecipe.mInputs[id], currentRecipe.mOreDictAlt[id]);
+ if (size < 0 || stack.stackSize < size * currentRecipeParallel) return false;
+ progress = mMaxProgresstime / currentInputLength;
+ stack.stackSize -= size * currentRecipeParallel;
+ mInputBusses.get(id).updateSlots();
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Slice{" + "id=" + id + ", progress=" + progress + '}';
+ }
+ }
+
+ private enum DataHatchElement implements IHatchElement<MTE_AdvAssLine> {
+
+ DataAccess;
+
+ @Override
+ public List<? extends Class<? extends IMetaTileEntity>> mteClasses() {
+ return Collections.singletonList(GT_MetaTileEntity_Hatch_DataAccess.class);
+ }
+
+ @Override
+ public IGT_HatchAdder<MTE_AdvAssLine> adder() {
+ return MTE_AdvAssLine::addDataAccessToMachineList;
+ }
+
+ @Override
+ public long count(MTE_AdvAssLine t) {
+ return t.mDataAccessHatches.size();
+ }
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_LinkedInputBus.java b/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_LinkedInputBus.java
new file mode 100644
index 0000000000..da0f10c2bb
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/mte/MTE_LinkedInputBus.java
@@ -0,0 +1,616 @@
+package net.glease.ggfab.mte;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+import net.glease.ggfab.GGConstants;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.WorldSavedData;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Color;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+import com.gtnewhorizons.modularui.common.widget.textfield.TextFieldWidget;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.tileentities.machines.IRecipeProcessingAwareHatch;
+
+public class MTE_LinkedInputBus extends GT_MetaTileEntity_Hatch_InputBus implements IRecipeProcessingAwareHatch {
+
+ public static final int SIZE_INVENTORY = 18;
+ private SharedInventory mRealInventory;
+ private final ItemStackHandlerProxy handler = new ItemStackHandlerProxy();
+ private String mChannel;
+ private boolean mPrivate;
+ private State mState;
+ private WorldSave save;
+
+ public MTE_LinkedInputBus(int id, String name, String nameRegional, int tier) {
+ super(
+ id,
+ name,
+ nameRegional,
+ tier,
+ 1,
+ new String[] { SIZE_INVENTORY + " slot input bus linked together wirelessly",
+ "Link does not cross world boundary",
+ "Left/right click with data stick to copy/paste configuration", GGConstants.GGMARK_TOOLTIP, });
+ }
+
+ public MTE_LinkedInputBus(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures);
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new MTE_LinkedInputBus(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return 0;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new TextFieldWidget().setSynced(true, true).setGetter(() -> mChannel == null ? "" : mChannel)
+ .setSetter(this::setChannel).setTextColor(Color.WHITE.dark(1))
+ .setTextAlignment(Alignment.CenterLeft).setBackground(GT_UITextures.BACKGROUND_TEXT_FIELD)
+ .setGTTooltip(() -> mTooltipCache.getData("ggfab.tooltip.linked_input_bus.change_freq_warn"))
+ .setSize(60, 18).setPos(48, 3))
+ .widget(
+ new CycleButtonWidget().setToggle(this::isPrivate, this::setPrivate)
+ .setTextureGetter(
+ i -> i == 1 ? GT_UITextures.OVERLAY_BUTTON_CHECKMARK
+ : GT_UITextures.OVERLAY_BUTTON_CROSS)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE).setSynced(true, true)
+ .setGTTooltip(() -> mTooltipCache.getData("ggfab.tooltip.linked_input_bus.private"))
+ .setSize(18, 18).setPos(150, 3))
+ .widget(
+ SlotGroup.ofItemHandler(handler, 9).startFromSlot(0).endAtSlot(SIZE_INVENTORY - 1)
+ .background(getGUITextureSet().getItemSlot())
+ .slotCreator(i -> new BaseSlot(handler, i, false) {
+
+ @Override
+ public ItemStack getStack() {
+ return isEnabled() ? super.getStack() : null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mChannel != null;
+ }
+ }).build().setPos(7, 24))
+ .widget(new TextWidget(new Text("Private")).setPos(110, 3).setSize(43, 20))
+ .widget(new TextWidget(new Text("Channel")).setPos(5, 3).setSize(43, 20));
+ }
+
+ @Override
+ public int getCircuitSlotX() {
+ return 152;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aIndex) {
+ if (aIndex == getCircuitSlot()) return super.getStackInSlot(aIndex);
+ if (mState != State.Blocked && mChannel != null && mRealInventory != null) {
+ if (aIndex > 0 && aIndex <= SIZE_INVENTORY) return mRealInventory.stacks[aIndex - 1];
+ }
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ if (aIndex == getCircuitSlot()) {
+ mInventory[0] = GT_Utility.copyAmount(0, aStack);
+ markDirty();
+ } else if (mState != State.Blocked && mChannel != null && mRealInventory != null) {
+ if (aIndex > 0 && aIndex <= SIZE_INVENTORY) {
+ mRealInventory.stacks[aIndex - 1] = aStack;
+ getWorldSave().markDirty();
+ }
+ }
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return super.getTexturesActive(aBaseTexture);
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return super.getTexturesInactive(aBaseTexture);
+ }
+
+ @Override
+ public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return isValidSlot(aIndex) && aStack != null
+ && mChannel != null
+ && mRealInventory != null
+ && aIndex > getCircuitSlot()
+ && aIndex < SIZE_INVENTORY + 1
+ && (mRealInventory.stacks[aIndex - 1] == null
+ || GT_Utility.areStacksEqual(aStack, mRealInventory.stacks[aIndex - 1]))
+ && allowPutStack(getBaseMetaTileEntity(), aIndex, ForgeDirection.getOrientation(ordinalSide), aStack);
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == getBaseMetaTileEntity().getFrontFacing() && aIndex != getCircuitSlot()
+ && (mRecipeMap == null || disableFilter || mRecipeMap.containsInput(aStack))
+ && (mRealInventory.disableLimited || limitedAllowPutStack(aIndex, aStack));
+ }
+
+ @Override
+ protected boolean limitedAllowPutStack(int aIndex, ItemStack aStack) {
+ for (int i = 0; i < SIZE_INVENTORY; i++)
+ if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get_nocopy(aStack), mRealInventory.stacks[i]))
+ return i == aIndex - 1;
+ return mRealInventory.stacks[aIndex - 1] == null;
+ }
+
+ @Override
+ public boolean canExtractItem(int aIndex, ItemStack aStack, int aSide) {
+ return false;
+ }
+
+ @Override
+ public int getSizeInventory() {
+ if (mState != State.Blocked && mChannel != null && mRealInventory != null) return SIZE_INVENTORY + 1;
+ return 1;
+ }
+
+ @Override
+ public void startRecipeProcessing() {
+ if (mRealInventory == null) return;
+ if (mRealInventory.used) {
+ mState = State.Blocked;
+ } else {
+ mRealInventory.used = true;
+ mState = State.Activated;
+ }
+ }
+
+ @Override
+ public CheckRecipeResult endRecipeProcessing(GT_MetaTileEntity_MultiBlockBase controller) {
+ if (mState == State.Activated) {
+ assert mRealInventory != null;
+ mRealInventory.used = false;
+ }
+ mState = State.Default;
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ @Override
+ public void updateSlots() {
+ if (mChannel == null || mRealInventory == null) return;
+ for (int i = 0; i < mRealInventory.stacks.length; i++) {
+ if (mRealInventory.stacks[i] != null
+ && (mRealInventory.stacks[i].getItem() == null || mRealInventory.stacks[i].stackSize <= 0))
+ mRealInventory.stacks[i] = null;
+ }
+ if (!mRealInventory.disableSort) fillStacksIntoFirstSlots();
+ markDirty();
+ getWorldSave().markDirty();
+ }
+
+ @Override
+ protected void fillStacksIntoFirstSlots() {
+ // sanity check
+ if (mRealInventory == null) return;
+ final int L = SIZE_INVENTORY;
+ HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(L);
+ HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(L);
+ List<GT_Utility.ItemId> order = new ArrayList<>(L);
+ List<Integer> validSlots = new ArrayList<>(L);
+ for (int i = 0; i < L; i++) {
+ validSlots.add(i);
+ ItemStack s = mRealInventory.stacks[i];
+ if (s == null) continue;
+ GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s);
+ slots.merge(sID, s.stackSize, Integer::sum);
+ if (!stacks.containsKey(sID)) stacks.put(sID, s);
+ order.add(sID);
+ mRealInventory.stacks[i] = null;
+ }
+ int slotindex = 0;
+ for (GT_Utility.ItemId sID : order) {
+ int toSet = slots.get(sID);
+ if (toSet == 0) continue;
+ int slot = validSlots.get(slotindex);
+ slotindex++;
+ mRealInventory.stacks[slot] = stacks.get(sID).copy();
+ toSet = Math.min(toSet, mRealInventory.stacks[slot].getMaxStackSize());
+ mRealInventory.stacks[slot].stackSize = toSet;
+ slots.merge(sID, toSet, (a, b) -> a - b);
+ }
+ }
+
+ private void dropItems(ItemStack[] aStacks) {
+ for (ItemStack stack : aStacks) {
+ if (!GT_Utility.isStackValid(stack)) continue;
+ EntityItem ei = new EntityItem(
+ getBaseMetaTileEntity().getWorld(),
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1) + 0.5,
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1) + 0.5,
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1) + 0.5,
+ stack);
+ ei.motionX = ei.motionY = ei.motionZ = 0;
+ getBaseMetaTileEntity().getWorld().spawnEntityInWorld(ei);
+ }
+ }
+
+ @Override
+ public boolean shouldDropItemAt(int index) {
+ // NOTE by this time onBlockDestroyed has already been called, i.e. so ref has already been decremented.
+ // so we really should check for ref <= 0 instead of ref <= 1
+ return mRealInventory != null && mRealInventory.ref <= 0;
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ super.onBlockDestroyed();
+ if (mRealInventory != null) {
+ if (--mRealInventory.ref <= 0) getWorldSave().remove(mChannel);
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (mChannel != null) aNBT.setString("channel", mChannel);
+ aNBT.setBoolean("private", mPrivate);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ String channel = aNBT.getString("channel");
+ if ("".equals(channel)) channel = null;
+ this.mChannel = channel;
+ mPrivate = aNBT.getBoolean("private");
+ }
+
+ public String getChannel() {
+ return mChannel;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (mChannel != null) {
+ mRealInventory = getWorldSave().get(getRealChannel());
+ handler.set(mRealInventory.stacks);
+ }
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!getBaseMetaTileEntity().getCoverBehaviorAtSideNew(side).isGUIClickable(
+ side,
+ getBaseMetaTileEntity().getCoverIDAtSide(side),
+ getBaseMetaTileEntity().getComplexCoverDataAtSide(side),
+ getBaseMetaTileEntity()))
+ return;
+ if (aPlayer.isSneaking()) {
+ if (this.mRealInventory == null) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.no_channel"));
+ return;
+ }
+ if (mRealInventory.disableSort) {
+ mRealInventory.disableSort = false;
+ } else {
+ if (mRealInventory.disableLimited) {
+ mRealInventory.disableLimited = false;
+ } else {
+ mRealInventory.disableSort = true;
+ mRealInventory.disableLimited = true;
+ }
+ }
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.hatch.disableSort." + mRealInventory.disableSort) + " "
+ + StatCollector
+ .translateToLocal("GT5U.hatch.disableLimited." + mRealInventory.disableLimited));
+ } else {
+ this.disableFilter = !this.disableFilter;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.hatch.disableFilter." + this.disableFilter));
+ }
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (!(aPlayer instanceof EntityPlayerMP))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ ItemStack stick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(stick, false, true))
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ if (!stick.hasTagCompound() || !"linkedinputbus".equals(stick.stackTagCompound.getString("ggfab.type"))) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.no_data"));
+ return true;
+ }
+ ItemStack circuit = GT_Utility.loadItem(stick.stackTagCompound, "circuit");
+ String channel = stick.stackTagCompound.getString("channel");
+ if (GT_Utility.isStackInvalid(circuit)) circuit = null;
+ if ("".equals(channel)) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.no_data"));
+ return true;
+ } else if (circuit != null && getConfigurationCircuits().stream().noneMatch(circuit::isItemEqual)) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.invalid_circuit"));
+ return true;
+ }
+ UUID owner = stick.stackTagCompound.hasKey("owner1")
+ ? new UUID(stick.stackTagCompound.getLong("owner1"), stick.stackTagCompound.getLong("owner2"))
+ : null;
+ if (owner != null && !owner.equals(getBaseMetaTileEntity().getOwnerUuid())) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.not_owned"));
+ return true;
+ }
+ setPrivate(owner != null);
+ setChannel(channel);
+ setInventorySlotContents(getCircuitSlot(), circuit);
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.data_pasted", channel));
+ return true;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!(aPlayer instanceof EntityPlayerMP)) return;
+ ItemStack stick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(stick, false, true)) return;
+ if (getChannel() == null) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.no_channel"));
+ return;
+ }
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setString("ggfab.type", "linkedinputbus");
+ tag.setString("channel", getChannel());
+ tag.setTag("circuit", GT_Utility.saveItem(getStackInSlot(getCircuitSlot())));
+ if (isPrivate()) {
+ tag.setLong("owner1", getBaseMetaTileEntity().getOwnerUuid().getMostSignificantBits());
+ tag.setLong("owner2", getBaseMetaTileEntity().getOwnerUuid().getLeastSignificantBits());
+ }
+ aPlayer.addChatMessage(new ChatComponentTranslation("ggfab.info.linked_input_bus.data_copied", getChannel()));
+ stick.stackTagCompound = tag;
+ stick.setStackDisplayName("Linked Input Bus configuration");
+ // abuse the title mechanism here. I assure you it will be fine (tm).
+ GT_Utility.ItemNBT.setBookTitle(stick, "Channel: " + getChannel());
+ if (getBaseMetaTileEntity().getOwnerName() != null)
+ GT_Utility.ItemNBT.setBookAuthor(stick, getBaseMetaTileEntity().getOwnerName());
+ }
+
+ private String getRealChannel() {
+ if (mChannel == null) return null;
+ if (mPrivate) return getBaseMetaTileEntity().getOwnerUuid() + mChannel;
+ return new UUID(0, 0) + mChannel;
+ }
+
+ public boolean isPrivate() {
+ return mPrivate;
+ }
+
+ public void setPrivate(boolean aPrivate) {
+ if (aPrivate == mPrivate) return;
+ if (getBaseMetaTileEntity().isClientSide()) {
+ mPrivate = aPrivate;
+ return;
+ }
+ if (this.mChannel == null) {
+ mPrivate = aPrivate;
+ return;
+ }
+ getWorldSave().markDirty();
+ if (--this.mRealInventory.ref <= 0) {
+ // last referrer, drop inventory
+ dropItems(mRealInventory.stacks);
+ getWorldSave().remove(getRealChannel());
+ }
+ mPrivate = aPrivate;
+ mRealInventory = getWorldSave().get(getRealChannel());
+ this.handler.set(mRealInventory.stacks);
+ mRealInventory.ref++;
+ getWorldSave().markDirty();
+ }
+
+ public void setChannel(String aChannel) {
+ if ("".equals(aChannel)) aChannel = null;
+ if (getBaseMetaTileEntity().isClientSide()) {
+ mChannel = aChannel;
+ return;
+ }
+ if (Objects.equals(this.mChannel, aChannel)) return; // noop
+ if (this.mChannel != null) {
+ if (--this.mRealInventory.ref <= 0) {
+ // last referrer, drop inventory
+ dropItems(mRealInventory.stacks);
+ getWorldSave().remove(getRealChannel());
+ }
+ }
+ if (aChannel == null) {
+ this.mChannel = null;
+ this.mRealInventory = null;
+ this.handler.setFake();
+ } else {
+ this.mChannel = aChannel;
+ this.mRealInventory = getWorldSave().get(getRealChannel());
+ this.handler.set(mRealInventory.stacks);
+ mRealInventory.ref++;
+ }
+ getWorldSave().markDirty();
+ }
+
+ private WorldSave getWorldSave() {
+ if (save == null) {
+ WorldSave save = (WorldSave) getBaseMetaTileEntity().getWorld()
+ .loadItemData(WorldSave.class, "LinkedInputBusses");
+ if (save == null) {
+ save = new WorldSave("LinkedInputBusses");
+ getBaseMetaTileEntity().getWorld().setItemData(save.mapName, save);
+ }
+ this.save = save;
+ }
+ return save;
+ }
+
+ private enum State {
+ Activated,
+ Blocked,
+ Default,
+ }
+
+ private static class SharedInventory {
+
+ private final ItemStack[] stacks;
+ /**
+ * Inventory wrapper for ModularUI
+ */
+ private final ItemStackHandler inventoryHandler;
+ public boolean disableLimited = true;
+ public boolean disableSort;
+ private boolean used;
+ private int ref;
+
+ public SharedInventory() {
+ this.stacks = new ItemStack[SIZE_INVENTORY];
+ inventoryHandler = new ItemStackHandler(stacks);
+ }
+
+ public SharedInventory(NBTTagCompound tag) {
+ this.stacks = new ItemStack[SIZE_INVENTORY];
+ inventoryHandler = new ItemStackHandler(stacks);
+
+ for (int i = 0; i < SIZE_INVENTORY; i++) {
+ String key = "" + i;
+ if (!tag.hasKey(key, Constants.NBT.TAG_COMPOUND)) continue;
+ stacks[i] = ItemStack.loadItemStackFromNBT(tag.getCompoundTag(key));
+ }
+
+ ref = tag.getInteger("ref");
+ disableLimited = tag.getBoolean("dl");
+ disableSort = tag.getBoolean("ds");
+ }
+
+ public NBTTagCompound save() {
+ NBTTagCompound tag = new NBTTagCompound();
+ for (int i = 0; i < SIZE_INVENTORY; i++) {
+ ItemStack stack = stacks[i];
+ if (stack == null) continue;
+ tag.setTag("" + i, stack.writeToNBT(new NBTTagCompound()));
+ }
+ tag.setInteger("ref", ref);
+ tag.setBoolean("ds", disableSort);
+ tag.setBoolean("dl", disableLimited);
+ return tag;
+ }
+ }
+
+ public static class WorldSave extends WorldSavedData {
+
+ private final Map<String, SharedInventory> data = new HashMap<>();
+
+ public WorldSave(String p_i2141_1_) {
+ super(p_i2141_1_);
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound tag) {
+ data.clear();
+ @SuppressWarnings("unchecked")
+ Set<Map.Entry<String, NBTBase>> set = tag.tagMap.entrySet();
+ for (Map.Entry<String, NBTBase> e : set) {
+ data.put(e.getKey(), new SharedInventory((NBTTagCompound) e.getValue()));
+ }
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound tag) {
+ for (Map.Entry<String, SharedInventory> e : data.entrySet()) {
+ if (e.getValue().ref > 0) tag.setTag(e.getKey(), e.getValue().save());
+ }
+ }
+
+ public SharedInventory get(Object channel) {
+ return data.computeIfAbsent(channel.toString(), k -> new SharedInventory());
+ }
+
+ public void remove(Object channel) {
+ data.remove(channel.toString());
+ markDirty();
+ }
+ }
+
+ private static class ItemStackHandlerProxy extends ItemStackHandler {
+
+ private static final ItemStack[] EMPTY = new ItemStack[SIZE_INVENTORY];
+ private boolean fake;
+
+ public ItemStackHandlerProxy() {
+ super(EMPTY);
+ fake = true;
+ }
+
+ public void setFake() {
+ set(EMPTY);
+ fake = true;
+ }
+
+ public boolean isFake() {
+ return fake;
+ }
+
+ public void set(ItemStack[] stacks) {
+ this.stacks = Arrays.asList(stacks);
+ fake = false;
+ }
+
+ @Override
+ public NBTTagCompound serializeNBT() {
+ NBTTagCompound tag = super.serializeNBT();
+ tag.setBoolean("fake", fake);
+ return tag;
+ }
+
+ @Override
+ public void deserializeNBT(NBTTagCompound nbt) {
+ super.deserializeNBT(nbt);
+ fake = nbt.getBoolean("fake");
+ }
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/mui/ClickableTextWidget.java b/gigagramfab/src/main/java/net/glease/ggfab/mui/ClickableTextWidget.java
new file mode 100644
index 0000000000..34287c2237
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/mui/ClickableTextWidget.java
@@ -0,0 +1,55 @@
+package net.glease.ggfab.mui;
+
+import java.util.Arrays;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.drawable.TextRenderer;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
+
+public class ClickableTextWidget extends ButtonWidget {
+
+ private Text caption;
+ private int maxLines = 1;
+ private int marginInLines = 1;
+
+ public ClickableTextWidget(Text caption) {
+ super();
+ this.caption = caption;
+ super.setBackground(caption);
+ }
+
+ public ClickableTextWidget setText(Text caption) {
+ this.caption = caption;
+ return this;
+ }
+
+ public ClickableTextWidget setMaxLines(int maxLines) {
+ this.maxLines = maxLines;
+ return this;
+ }
+
+ public ClickableTextWidget setMarginInLines(int margin) {
+ this.marginInLines = margin;
+ return this;
+ }
+
+ @Override
+ public Widget setBackground(IDrawable... drawables) {
+ IDrawable[] all = Arrays.copyOf(drawables, drawables.length + 1);
+ all[drawables.length] = caption;
+ return super.setBackground(all);
+ }
+
+ @Override
+ protected @NotNull Size determineSize(int maxWidth, int maxHeight) {
+ if (caption == null) return super.determineSize(maxWidth, maxHeight);
+ return new Size(
+ Math.min(maxWidth, TextRenderer.getFontRenderer().getStringWidth(caption.getFormatted())),
+ (maxLines + marginInLines) * TextRenderer.getFontRenderer().FONT_HEIGHT);
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/util/GGUtils.java b/gigagramfab/src/main/java/net/glease/ggfab/util/GGUtils.java
new file mode 100644
index 0000000000..59dbf482ec
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/util/GGUtils.java
@@ -0,0 +1,75 @@
+package net.glease.ggfab.util;
+
+import java.util.StringJoiner;
+
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GGUtils {
+
+ public static boolean isValidTile(IGregTechTileEntity tile) {
+ return tile != null && !tile.isDead()
+ && tile.getMetaTileEntity() != null
+ && tile.getMetaTileEntity().getBaseMetaTileEntity() == tile;
+ }
+
+ public static boolean isValidTile(IMetaTileEntity mte) {
+ return mte != null && mte.getBaseMetaTileEntity() != null
+ && mte.getBaseMetaTileEntity().getMetaTileEntity() == mte
+ && !mte.getBaseMetaTileEntity().isDead();
+ }
+
+ public static ChunkCoordinates translate(ChunkCoordinates origin, ForgeDirection direction) {
+ return new ChunkCoordinates(
+ origin.posX + direction.offsetX,
+ origin.posY + direction.offsetY,
+ origin.posZ + direction.offsetZ);
+ }
+
+ public static String formatTileInfo(String prefix, IMetaTileEntity mte, String delimiter, String suffix) {
+ if (!isValidTile(mte)) return prefix + "N/A" + suffix;
+ StringJoiner sj = new StringJoiner(delimiter, prefix, suffix);
+ IGregTechTileEntity til = mte.getBaseMetaTileEntity();
+ sj.add(String.valueOf(til.getXCoord()));
+ sj.add(String.valueOf(til.getYCoord()));
+ sj.add(String.valueOf(til.getZCoord()));
+ return sj.toString();
+ }
+
+ public static String formatTileInfo(String prefix, IGregTechTileEntity tile, String delimiter, String suffix) {
+ if (!isValidTile(tile)) return prefix + "N/A" + suffix;
+ StringJoiner sj = new StringJoiner(delimiter, prefix, suffix);
+ sj.add(String.valueOf(tile.getXCoord()));
+ sj.add(String.valueOf(tile.getYCoord()));
+ sj.add(String.valueOf(tile.getZCoord()));
+ return sj.toString();
+ }
+
+ /**
+ * convert lowerCamelCase to any of snake case or normal sentence
+ */
+ public static String processSentence(String src, Character separator, boolean capitalize, boolean firstCapitalize) {
+ if (src == null) throw new IllegalArgumentException();
+ if (src.isEmpty()) return "";
+ StringBuilder out = new StringBuilder(src.length());
+ if (firstCapitalize) out.append(Character.toUpperCase(src.charAt(0)));
+ else out.append(src.charAt(0));
+ for (int i = 1; i < src.length(); i++) {
+ char ch = src.charAt(i);
+ if (Character.isUpperCase(ch)) {
+ if (separator != null) out.append(separator.charValue());
+ if (capitalize) {
+ out.append(ch);
+ } else {
+ out.append(Character.toLowerCase(ch));
+ }
+ } else {
+ out.append(ch);
+ }
+ }
+ return out.toString();
+ }
+}
diff --git a/gigagramfab/src/main/java/net/glease/ggfab/util/OverclockHelper.java b/gigagramfab/src/main/java/net/glease/ggfab/util/OverclockHelper.java
new file mode 100644
index 0000000000..bd75a02269
--- /dev/null
+++ b/gigagramfab/src/main/java/net/glease/ggfab/util/OverclockHelper.java
@@ -0,0 +1,75 @@
+package net.glease.ggfab.util;
+
+import gregtech.api.util.GT_Utility;
+
+public class OverclockHelper {
+
+ public static OverclockOutput normalOverclock(long recipeEUt, int duration, long inputVoltage, boolean perfectOC) {
+ if (recipeEUt > inputVoltage) return null;
+ int recipeTier = Math.max(1, GT_Utility.getTier(recipeEUt)); // ULV no overclock
+ int machineTier = GT_Utility.getTier(inputVoltage);
+ int shift = perfectOC ? 2 : 1;
+ while (recipeTier < machineTier && duration > 1) {
+ duration >>= shift;
+ recipeEUt <<= 2;
+ recipeTier++;
+ }
+ return new OverclockOutput(recipeEUt, duration);
+ }
+
+ public static OverclockOutput laserOverclock(long recipeEUt, int duration, long inputEUt,
+ float penaltyIncreaseFactor) {
+ if (recipeEUt > inputEUt) return null;
+ float currentPenalty = 4 + penaltyIncreaseFactor;
+ // 2/(n+k) overclock until energy hatch is crying
+ // must ensure it doesn't go to negative after overclock
+ while (recipeEUt * currentPenalty > 0 && recipeEUt * currentPenalty < inputEUt && duration > 1) {
+ duration >>= 1;
+ recipeEUt *= currentPenalty;
+ currentPenalty += penaltyIncreaseFactor;
+ }
+ return new OverclockOutput(recipeEUt, duration);
+ }
+
+ public static final class OverclockOutput {
+
+ private final long mEUt;
+ private final int mDuration;
+
+ public OverclockOutput(long aEUt, int aDuration) {
+ this.mEUt = aEUt;
+ this.mDuration = aDuration;
+ }
+
+ public long getEUt() {
+ return mEUt;
+ }
+
+ public int getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public String toString() {
+ return mEUt + "@" + mDuration + "ticks";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OverclockOutput)) return false;
+
+ OverclockOutput that = (OverclockOutput) o;
+
+ if (mEUt != that.mEUt) return false;
+ return mDuration == that.mDuration;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (mEUt ^ (mEUt >>> 32));
+ result = 31 * result + mDuration;
+ return result;
+ }
+ }
+}
diff --git a/gigagramfab/src/main/resources/LICENSE b/gigagramfab/src/main/resources/LICENSE
new file mode 100644
index 0000000000..63b4b681cb
--- /dev/null
+++ b/gigagramfab/src/main/resources/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) [year] [fullname]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/gigagramfab/src/main/resources/META-INF/ggfab_at.cfg b/gigagramfab/src/main/resources/META-INF/ggfab_at.cfg
new file mode 100644
index 0000000000..a6dc456eb5
--- /dev/null
+++ b/gigagramfab/src/main/resources/META-INF/ggfab_at.cfg
@@ -0,0 +1,2 @@
+public net.minecraft.nbt.NBTTagList field_74747_a # tagList
+public net.minecraft.nbt.NBTTagCompound field_74784_a # tagMap \ No newline at end of file
diff --git a/gigagramfab/src/main/resources/assets/ggfab/lang/en_US.lang b/gigagramfab/src/main/resources/assets/ggfab/lang/en_US.lang
new file mode 100644
index 0000000000..14b4343dfe
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/lang/en_US.lang
@@ -0,0 +1,41 @@
+ggfab.recipe.toolcast=Tool Casting Machine
+
+ggfab.info.advassline.0=Advanced Assembly Line Help
+ggfab.info.advassline.1=This is advanced assembly line from §6GigaGram§rFab.
+ggfab.info.advassline.2=It supports item pipelining. That is, it will mimic a real assembly line by consuming ingredients one by one instead of all at start. In effect, it offers a parallelism up to however many item input this recipe requires.
+ggfab.info.advassline.3=You can think of an advanced assembly line as a collection of assembly slices. Each assembly slice is capably of processing each step independent of other slices.
+ggfab.info.advassline.4=It will start processing once the input bus contents align with any stored data stick. The first slice will consume the input in Bus #1. After (recipe time/number of inputs) time, the first slice's work is concluded and will start the second slice. At the same time, first slice will look for input in input bus #1. If there are still enough input there slice #1 will start working again.
+ggfab.info.advassline.5=The terminal slice (the n-th slice, where n is number of item input in recipe) will put the recipe output in output bus when it has concluded his work. Whenever a non-terminal slice finished its work, it will try to pass the work onto next slice. If the next slice cannot find the materials in its input bus, the just-finished slice will remain in §4STUCK§r state and hang the assembly line. To help locate these §4STUCK§r assembly lines, the controller's front face will have its status light turned orange.
+ggfab.info.advassline.6=The EU/t cost of this machine is number of slices active multiplied by the original recipe EU/t. §4STUCK§r slices do not consume power. It will use the worst energy supplying hatch's input voltage for recipe tier calculation and normal imperfect overclock.
+ggfab.info.advassline.7=With exotic energy hatches, it can overclock beyond usual voltage tier, but will consume even more power than usual imperfect overclock. Every §2laser overclock§r will add %s to power exponent.
+ggfab.info.advassline.8=1 §2laser overclock§r will have 50%% recipe time and use %s%% power. 2 §2laser overclock§r will have 25%% recipe time and use %s%% (%s * %s) power. Will not overclock beyond 1 tick. Machine first tries to parallelize, then normal imperfect overclock, then §2laser overclock§r.
+ggfab.waila.advassline.slice=Slice #%s: %s s / %s s
+ggfab.waila.advassline.slice.small=Slice #%s: %s ticks / %s ticks
+ggfab.waila.advassline.slice.idle=Slice #%s: Idle
+ggfab.waila.advassline.slice.stuck=Slice #%s: §4STUCK
+ggfab.gui.advassline.slice.idle=Slice #%s: Idle
+ggfab.gui.advassline.slice.stuck=Slice #%s: §4STUCK
+ggfab.gui.advassline.slice.unknown=Slice #%s: ?
+ggfab.gui.advassline.shutdown=§4Last abnormal shutdown reason:
+ggfab.gui.advassline.shutdown_clear=§6Clear
+ggfab.gui.advassline.shutdown.input_busses=§4Too few input busses to support current recipe
+ggfab.gui.advassline.shutdown.recipe_null=§4Recipe is null (report to author)
+ggfab.gui.advassline.shutdown.fluid_null=§4Fluid drained failed (check your ME fluid storage)
+ggfab.gui.advassline.shutdown.structure=§4Incompatible structural change
+ggfab.gui.advassline.shutdown.energy=§4Insufficient power
+ggfab.gui.advassline.shutdown.load.energy=§4Loaded invalid electric stat
+ggfab.gui.advassline.shutdown.load.recipe=§4Incompatible recipe change
+
+ggfab.info.linked_input_bus.not_owned=§6The settings were copied from a different owner!
+ggfab.info.linked_input_bus.no_data=§6Configuration data not found!
+ggfab.info.linked_input_bus.invalid_data=§6Configuration data was found but it is invalid!
+ggfab.info.linked_input_bus.invalid_circuit=§6Configuration data was found but it contains a circuit not valid on this bus!
+ggfab.info.linked_input_bus.data_pasted=§2Pasted channel ("%s§2") and configuration circuit setting!
+ggfab.info.linked_input_bus.data_copied=§2Copied channel ("%s§2") and configuration circuit setting!
+ggfab.info.linked_input_bus.no_channel=§6No channel specified yet!
+
+ggfab.info.biome=Biome:
+
+ggfab.tooltip.linked_input_bus.change_freq_warn=Changing channel while this is the last one on this channel will spill all items left!
+ggfab.tooltip.linked_input_bus.private=Private channel are not shared with others.
+ggfab.tooltip.linked_input_bus.private.1=Changing channel while this is the last one on this channel will spill all items left!
diff --git a/gigagramfab/src/main/resources/assets/ggfab/lang/zh_CN.lang b/gigagramfab/src/main/resources/assets/ggfab/lang/zh_CN.lang
new file mode 100644
index 0000000000..c5a2da901d
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/lang/zh_CN.lang
@@ -0,0 +1,28 @@
+ggfab.info.advassline.0=高级装配线教程
+ggfab.info.advassline.1=爱来自 §6GigaGram§rFab 与 w
+ggfab.info.advassline.2=这种新式装配线支持流水线式装配,也就是说,这种装配线会逐一从输入总线中获取原材料,而不是像其他GT机器一样在启动时获取所有原材料。这种工作模式使得他获得了等同与当前配方物品输入数量的并行数。
+ggfab.info.advassline.3=你可以将高级装配线想象成一组装配刀片机,每个装配刀片可以互相独立的工作。
+ggfab.info.advassline.4=高级装配线会在输入总线第一格的内容与任意一个闪存记录的配方相符合时开始工作。第一装配刀片会从第一个输入总线获取输入。在(配方时间/物品输入个数)的时间后,第一装配刀片的工作结束了,之后便会将半成品转交给第二装配刀片,然后第二装配刀片再从自己的输入总线中获取输入。于此同时,在第二装配刀片工作时,第一装配刀片会继续尝试从第一输入总线中获取输入,如果这里仍然有足够的输入,则第一装配刀片会直接开始工作。
+ggfab.info.advassline.5=如果一个配方有n个输入,那么第N个装配刀片就是最终装配刀片,在他完成工作时就会将成品放到末尾的输出总线中。除最终装配刀片外,其他装配刀片在自己的工作结束时都会试图将半成品交给下一个刀片。如果下一个刀片无法从自己的输入总线中找到输入,则上一片刀片会停留在§4停滞§r状态,并最终使整个装配线阻塞。为了帮助你发现阻塞的装配线,任何含有§4停滞§r状态的装配刀片的装配线将会亮起橙红色指示灯。
+ggfab.info.advassline.6=高级装配线的瞬时能耗是正在工作的装配刀片的数量乘以当前配方的EU/t。§4停滞§r状态的装配刀片不会消耗能量。额外需要注意的是,本机器会使用所有能量供应舱室中最低的电压等级来计算配方等级与普通的非完美超频。
+ggfab.info.advassline.7=在使用一些特殊的能量供应舱室时,高级装配线可以进行额外的超频(称为§2激光超频§r),但是会使用比非完美超频更多的能量。每个§2激光超频§r会使得能量指数增加%s。
+ggfab.info.advassline.8=一层激光超频会使配方时间变为50%%,但会使用%s%%能量。二层激光超频会使配方时间变为25%%,但会使用%s%% (%s * %s)能量。激光超频仍然不会越过1tick极限。高级装配线在计算超频时会优先考虑并行,然后考虑非完美超频,最后进行§2激光超频§r。
+ggfab.waila.advassline.slice=装配刀片 #%s: %s秒 / %s秒
+ggfab.waila.advassline.slice.small=装配刀片 #%s: %s刻 / %s刻
+ggfab.waila.advassline.slice.idle=装配刀片 #%s: 空闲
+ggfab.waila.advassline.slice.stuck=装配刀片 #%s: §4停滞
+ggfab.gui.advassline.slice.idle=装配刀片 #%s: 空闲
+ggfab.gui.advassline.slice.stuck=装配刀片 #%s: §4停滞
+ggfab.gui.advassline.slice.unknown=装配刀片 #%s: ?
+
+ggfab.info.linked_input_bus.not_owned=§6记录的设置来自于不同的拥有者!
+ggfab.info.linked_input_bus.invalid_data=§6找到了配置,但是它已不再有效!
+ggfab.info.linked_input_bus.data_pasted=§2已粘贴频道("%s§2")和编程芯片配置!
+ggfab.info.linked_input_bus.data_copied=§2已记录频道("%s§2")和编程芯片配置!
+ggfab.info.linked_input_bus.no_channel=§6还没有设置频道!
+
+ggfab.info.biome=群系:
+
+ggfab.tooltip.linked_input_bus.change_freq_warn=如果这是最后一个本频道的输入总线,改变频道会使剩余的内容物洒在地上!
+ggfab.tooltip.linked_input_bus.private=私有频道不会与其余玩家共享
+ggfab.tooltip.linked_input_bus.private.1=如果这是最后一个本频道的输入总线,改变频道会使剩余的内容物洒在地上! \ No newline at end of file
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE.png
new file mode 100644
index 0000000000..da0ce84a35
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE.png
new file mode 100644
index 0000000000..89277c79d6
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW.png
new file mode 100644
index 0000000000..4a8ad42dd8
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_ACTIVE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_GLOW.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_GLOW.png
new file mode 100644
index 0000000000..1227d5a7fd
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK.png
new file mode 100644
index 0000000000..1c6e16c428
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW.png b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW.png
new file mode 100644
index 0000000000..f5e67e7668
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/blocks/iconsets/OVERLAY_FRONT_ADV_ASSLINE_STUCK_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/0.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/0.png
new file mode 100644
index 0000000000..5a0b113068
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/0.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/1.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/1.png
new file mode 100644
index 0000000000..7be53426b1
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/1.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/2.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/2.png
new file mode 100644
index 0000000000..c80ecd6158
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/2.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/3.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/3.png
new file mode 100644
index 0000000000..7aaa51f26f
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/3.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/30.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/30.png
new file mode 100644
index 0000000000..d636a1ca28
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/30.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/31.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/31.png
new file mode 100644
index 0000000000..174f2cbad8
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/31.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/32.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/32.png
new file mode 100644
index 0000000000..310ea4ee20
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/32.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/33.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/33.png
new file mode 100644
index 0000000000..ff6b25d82d
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/33.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/34.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/34.png
new file mode 100644
index 0000000000..8bde5e4213
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/34.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/35.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/35.png
new file mode 100644
index 0000000000..20fc1a2870
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/35.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/36.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/36.png
new file mode 100644
index 0000000000..951edab117
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/36.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/4.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/4.png
new file mode 100644
index 0000000000..f3ecd55264
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/4.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/5.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/5.png
new file mode 100644
index 0000000000..c9e4664ed0
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/5.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/6.png b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/6.png
new file mode 100644
index 0000000000..ca649b92ea
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/ggfab/textures/items/gt.ggfab.d1/6.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_ACTIVE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_BOTTOM_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT.png
new file mode 100644
index 0000000000..06eb7d6066
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE.png
new file mode 100644
index 0000000000..90ee9766bb
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE_GLOW.png
new file mode 100644
index 0000000000..99d60b554e
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_ACTIVE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_FRONT_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_ACTIVE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_SIDE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_ACTIVE_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_GLOW.png b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_GLOW.png
new file mode 100644
index 0000000000..a2fcfb9a21
--- /dev/null
+++ b/gigagramfab/src/main/resources/assets/gregtech/textures/blocks/basicmachines/tool_cast/OVERLAY_TOP_GLOW.png
Binary files differ
diff --git a/gigagramfab/src/main/resources/mcmod.info b/gigagramfab/src/main/resources/mcmod.info
new file mode 100644
index 0000000000..85a730d971
--- /dev/null
+++ b/gigagramfab/src/main/resources/mcmod.info
@@ -0,0 +1,18 @@
+[
+ {
+ "modid": "${modId}",
+ "name": "${modName}",
+ "description": "Production at scale",
+ "version": "${modVersion}",
+ "mcversion": "${minecraftVersion}",
+ "url": "https://glease.github.io/GigaGramFab",
+ "updateUrl": "",
+ "authorList": ["glee8e"],
+ "credits": "",
+ "logoFile": "",
+ "screenshots": [],
+ "dependencies": [
+ "gregtech"
+ ]
+ }
+]
diff --git a/gigagramfab/src/test/java/net/glease/ggfab/util/OverclockHelperTest.java b/gigagramfab/src/test/java/net/glease/ggfab/util/OverclockHelperTest.java
new file mode 100644
index 0000000000..da030dadc8
--- /dev/null
+++ b/gigagramfab/src/test/java/net/glease/ggfab/util/OverclockHelperTest.java
@@ -0,0 +1,47 @@
+package net.glease.ggfab.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+class OverclockHelperTest {
+
+ @Test
+ void normalOverclockImperfect() {
+ // fails recipe
+ assertNull(OverclockHelper.normalOverclock(10000, 10000, 1, false));
+ // no overclock
+ assertEquals(new OverclockHelper.OverclockOutput(30, 64), OverclockHelper.normalOverclock(30, 64, 32, false));
+ // imperfect overclock
+ assertEquals(new OverclockHelper.OverclockOutput(120, 32), OverclockHelper.normalOverclock(30, 64, 128, false));
+ // lots of overclock
+ assertEquals(
+ new OverclockHelper.OverclockOutput(30720, 2),
+ OverclockHelper.normalOverclock(30, 64, 32768, false));
+ // do not overclock beyond useful
+ assertEquals(
+ new OverclockHelper.OverclockOutput(122880, 1),
+ OverclockHelper.normalOverclock(30, 64, 524288, false));
+ }
+
+ @Test
+ void laserOverclock() {
+ // fails recipe
+ assertNull(OverclockHelper.laserOverclock(10000, 10000, 1, 5));
+ // no overclock
+ assertEquals(new OverclockHelper.OverclockOutput(30, 64), OverclockHelper.laserOverclock(30, 64, 32, 0.5f));
+ // 0.3 amp overclock. 0.25 amp would be not in current tier so no point in testing that
+ assertEquals(new OverclockHelper.OverclockOutput(10, 64), OverclockHelper.laserOverclock(10, 64, 32, 0.5f));
+ // laser overclock
+ assertEquals(
+ new OverclockHelper.OverclockOutput(135, 32),
+ OverclockHelper.laserOverclock(30, 64, 32 * 16, 0.5f));
+ // lots of overclock
+ assertEquals(
+ new OverclockHelper.OverclockOutput(22272, 4),
+ OverclockHelper.laserOverclock(30, 64, 32 * 1024, 0.5f));
+ // do not overclock beyond useful
+ assertEquals(new OverclockHelper.OverclockOutput(135, 1), OverclockHelper.laserOverclock(30, 2, 32 * 16, 0.5f));
+ }
+}