From 1f8150d82145af06fade544486ce6dce4bd2dea7 Mon Sep 17 00:00:00 2001 From: heldplayer Date: Tue, 1 Aug 2017 00:46:31 +0200 Subject: [PATCH] First commit! --- .gitignore | 17 ++ build.gradle | 114 +++++++++++ gradle.properties | 8 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 52271 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 +++++++++++++++ gradlew.bat | 90 +++++++++ .../me/heldplayer/mystcraft_jei/Assets.java | 11 + .../heldplayer/mystcraft_jei/CommonProxy.java | 44 ++++ .../mystcraft_jei/InkMixerOutput.java | 51 +++++ .../mystcraft_jei/ItemDescriptions.java | 137 +++++++++++++ .../heldplayer/mystcraft_jei/JEIPlugin.java | 172 ++++++++++++++++ .../mystcraft_jei/MystcraftJEI.java | 31 +++ .../me/heldplayer/mystcraft_jei/Objects.java | 21 ++ .../me/heldplayer/mystcraft_jei/Util.java | 14 ++ .../mystcraft_jei/client/ClientProxy.java | 51 +++++ .../client/event/GuiEventHandler.java | 74 +++++++ .../client/gui/DrawableInkEyeOverlay.java | 66 ++++++ .../client/gui/DrawablePageList.java | 47 +++++ .../client/gui/DrawableRecipesButton.java | 31 +++ .../client/gui/DrawableTankBackground.java | 43 ++++ .../client/gui/DrawableTextField.java | 54 +++++ .../client/gui/EmptyDrawable.java | 41 ++++ .../client/gui/IDrawableTextfield.java | 10 + .../client/gui/PageRenderer.java | 79 ++++++++ .../crafting/BookBinderRecipeCategory.java | 66 ++++++ .../crafting/BookBinderRecipes.java | 71 +++++++ .../crafting/InkMixerRecipeCategory.java | 95 +++++++++ .../crafting/InkMixerRecipeWrapper.java | 138 +++++++++++++ .../crafting/InkMixerRecipes.java | 80 ++++++++ .../crafting/LinkingBookRecipeWrapper.java | 32 +++ .../crafting/MystcraftRecipeCategoryUid.java | 11 + .../crafting/WritingDeskRecipeCategory.java | 95 +++++++++ .../crafting/WritingDeskRecipes.java | 184 +++++++++++++++++ .../mystcraft/MystItemFactory.java | 28 +++ .../mystcraft/MystLinkProperty.java | 31 +++ .../integration/mystcraft/MystPage.java | 24 +++ .../integration/mystcraft/MystRender.java | 30 +++ .../integration/mystcraft/MystSymbol.java | 30 +++ .../mystcraft/MystcraftIntegration.java | 87 ++++++++ .../subtypes/PageSubtypeInterpreter.java | 48 +++++ .../subtypes/UnlinkedLinkbookInterpreter.java | 40 ++++ .../heldplayer/mystcraft_jei/util/Entry.java | 11 + .../mystcraft_jei/util/Integration.java | 188 ++++++++++++++++++ .../mystcraft-jei-plugin/lang/en_US.lang | 79 ++++++++ .../textures/gui/ages.png | Bin 0 -> 1314 bytes .../textures/gui/icons_extra.png | Bin 0 -> 270 bytes .../textures/spacore/view-age.png | Bin 0 -> 163 bytes src/main/resources/mcmod.info | 13 ++ 49 files changed, 2757 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/Assets.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/CommonProxy.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/InkMixerOutput.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/ItemDescriptions.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/JEIPlugin.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/MystcraftJEI.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/Objects.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/Util.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/ClientProxy.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/event/GuiEventHandler.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableInkEyeOverlay.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawablePageList.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableRecipesButton.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTankBackground.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTextField.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/EmptyDrawable.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/IDrawableTextfield.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/client/gui/PageRenderer.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipeCategory.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipes.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeCategory.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeWrapper.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipes.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/LinkingBookRecipeWrapper.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/MystcraftRecipeCategoryUid.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipeCategory.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipes.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystItemFactory.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystLinkProperty.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystPage.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystRender.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystSymbol.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystcraftIntegration.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/subtypes/PageSubtypeInterpreter.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/subtypes/UnlinkedLinkbookInterpreter.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/util/Entry.java create mode 100644 src/main/java/me/heldplayer/mystcraft_jei/util/Integration.java create mode 100644 src/main/resources/assets/mystcraft-jei-plugin/lang/en_US.lang create mode 100644 src/main/resources/assets/mystcraft-jei-plugin/textures/gui/ages.png create mode 100644 src/main/resources/assets/mystcraft-jei-plugin/textures/gui/icons_extra.png create mode 100644 src/main/resources/assets/mystcraft-jei-plugin/textures/spacore/view-age.png create mode 100644 src/main/resources/mcmod.info diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf054a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.tmp +*.bak +*.swp +*~.nib +*.log +*.lck +*.bin +*.iml +*.ipr +*.iws + +/.gradle/ +/.idea/ +/build/ +/classes/ +/run/ +/libs/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5ba8756 --- /dev/null +++ b/build.gradle @@ -0,0 +1,114 @@ +buildscript { + repositories { + mavenCentral() + maven { url = "http://files.minecraftforge.net/maven" } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' + } +} +apply plugin: 'net.minecraftforge.gradle.forge' +//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. + +repositories { + maven { + name = "Progwml6 maven" + url = "http://dvs1.progwml6.com/files/maven" + } +} + +sourceCompatibility = targetCompatibility = "1.8" +compileJava { + sourceCompatibility = targetCompatibility = "1.8" +} + +println "$project.name for Forge: ${minecraft_version}-${forge_version}" + +version = "${minecraft_version}-${super_number}.${major_number}.${minor_number}${postfix}" +group = "me.heldplayer.plugins.jei.mystcraft" +archivesBaseName = "mystcraft-jei" + +minecraft { + version = minecraft_version + "-" + forge_version + runDir = "run" + + useDepAts = true + makeObfSourceJar = false + + // the mappings can be changed at any time, and must be in the following format. + // snapshot_YYYYMMDD snapshot are built nightly. + // stable_# stables are built at the discretion of the MCP team. + // Use non-default mappings at your own risk. they may not always work. + // simply re-run your setup task after changing the mappings to update your workspace. + mappings = "snapshot_20161220" + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. + + if (project.hasProperty("signature")) + replace "@FINGERPRINT@", project.signature +} + +dependencies { + deobfProvided "mezz.jei:jei_${minecraft_version}:${jei_version}:api" + runtime "mezz.jei:jei_${minecraft_version}:${jei_version}" + + // you may put jars on which you depend on in ./libs + // or you may define them like so.. + //compile "some.group:artifact:version:classifier" + //compile "some.group:artifact:version" + + // real examples + //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env + //compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env + + // the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. + //provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' + + // the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided, + // except that these dependencies get remapped to your current MCP mappings + //deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev' + //deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev' + + // for more info... + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html +} + +processResources { + // this will ensure that this task is redone when the versions change. + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.version + + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info', 'version.properties' + + // replace version and mcversion + expand 'version': "${super_number}.${major_number}.${minor_number}${postfix}", + 'mcversion': "${minecraft_version}" + } + + // copy everything else except the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info', 'version.properties' + } + + rename '(.+_at.cfg)', 'META-INF/$1' +} + +// https://gist.github.com/matthewprenger/9b2da059b89433a01c1c +task signJar(type: SignJar, dependsOn: reobfJar) { + onlyIf { project.hasProperty('keyStore') } + + if (!project.hasProperty('keyStore')) { // This shouldn't be possible, but onlyIf isn't working properly :/ + throw new StopExecutionException() + } + + keyStore = project.keyStore // This needs to be a path to the keystore file + alias = project.keyStoreAlias + storePass = project.keyStorePass + keyPass = project.keyStoreKeyPass + inputFile = jar.archivePath + outputFile = jar.archivePath +} + +build.dependsOn signJar diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a6ad02c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,8 @@ +minecraft_version=1.11.2 +forge_version=13.20.1.2386 +jei_version=4.5.0.294 + +super_number=1 +major_number=0 +minor_number=0 +postfix= diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..30d399d8d2bf522ff5de94bf434a7cc43a9a74b5 GIT binary patch literal 52271 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 p \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +: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 %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/me/heldplayer/mystcraft_jei/Assets.java b/src/main/java/me/heldplayer/mystcraft_jei/Assets.java new file mode 100644 index 0000000..1e2a173 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/Assets.java @@ -0,0 +1,11 @@ +package me.heldplayer.mystcraft_jei; + +import net.minecraft.util.ResourceLocation; + +public class Assets { + public static final ResourceLocation BOOK_PAGE_LEFT = new ResourceLocation("mystcraft:gui/bookui_pageL.png"); + + public static final ResourceLocation ICONS_EXTRA = new ResourceLocation(Objects.MOD_ID + ":textures/gui/icons_extra.png"); + public static final int ICONS_EXTRA_WIDTH = 32; + public static final int ICONS_EXTRA_HEIGHT = 32; +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/CommonProxy.java b/src/main/java/me/heldplayer/mystcraft_jei/CommonProxy.java new file mode 100644 index 0000000..9f2f5dc --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/CommonProxy.java @@ -0,0 +1,44 @@ +package me.heldplayer.mystcraft_jei; + +import me.heldplayer.mystcraft_jei.client.gui.EmptyDrawable; +import me.heldplayer.mystcraft_jei.client.gui.IDrawableTextfield; +import me.heldplayer.mystcraft_jei.crafting.InkMixerRecipeWrapper; +import mezz.jei.api.gui.IDrawable; + +import javax.annotation.Nonnull; + +public class CommonProxy { + private IDrawable recipeIcon = new EmptyDrawable(16, 16); + + public void registerEventHandlers() { + } + + @Nonnull + public IDrawableTextfield createTextField(int width, int height) { + return new EmptyDrawable(width, height); + } + + @Nonnull + public IDrawable createInkEyeOverlay(@Nonnull IDrawable other, @Nonnull InkMixerRecipeWrapper recipe) { + return new EmptyDrawable(other.getWidth(), other.getHeight()); + } + + @Nonnull + public IDrawable createTankBackground(int width, int height) { + return new EmptyDrawable(width, height); + } + + @Nonnull + public IDrawable createPageList(int width, int height) { + return new EmptyDrawable(width, height); + } + + public void setRecipeIcon(@Nonnull IDrawable icon) { + this.recipeIcon = icon; + } + + @Nonnull + public IDrawable getRecipeIcon() { + return this.recipeIcon; + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/InkMixerOutput.java b/src/main/java/me/heldplayer/mystcraft_jei/InkMixerOutput.java new file mode 100644 index 0000000..6338d35 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/InkMixerOutput.java @@ -0,0 +1,51 @@ +package me.heldplayer.mystcraft_jei; + +import com.xcompwiz.mystcraft.api.util.ColorGradient; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystLinkProperty; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + +public class InkMixerOutput { + private final ColorGradient gradient; + private final String[] modifiers; + + public InkMixerOutput(@Nullable ColorGradient gradient, @Nonnull String[] modifiers) { + this.gradient = modifiers.length > 0 ? gradient : null; + this.modifiers = modifiers; + } + + @Nullable + public ColorGradient getGradient() { + return gradient; + } + + @Nonnull + public String[] getModifiers() { + return modifiers; + } + + @Nullable + public static InkMixerOutput getOutputFor(@Nonnull ItemStack input) { + if (!MystLinkProperty.isReady()) { + return null; + } + + Map properties = MystLinkProperty.getPropertiesForItem(input); + + if (properties == null) { + return null; + } + + String[] modifiers = properties.keySet().stream() + .filter(java.util.Objects::nonNull) + .filter((mod) -> !mod.isEmpty()) + .toArray(String[]::new); + + ColorGradient gradient = modifiers.length > 0 ? MystLinkProperty.getPropertiesGradient(properties) : null; + + return new InkMixerOutput(gradient, modifiers); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/ItemDescriptions.java b/src/main/java/me/heldplayer/mystcraft_jei/ItemDescriptions.java new file mode 100644 index 0000000..5683d31 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/ItemDescriptions.java @@ -0,0 +1,137 @@ +package me.heldplayer.mystcraft_jei; + +import com.xcompwiz.mystcraft.api.MystObjects; +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.IModRegistry; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; + +public class ItemDescriptions { + + private static final Map>, String[]> DESCRIPTIONS = new LinkedHashMap<>(); + + public static void initDescriptions() { + ItemDescriptions.DESCRIPTIONS.clear(); + + // Decay blocks + { + List> list = new ArrayList<>(); + list.add(Pair.of(MystObjects.Blocks.decay, 0)); + list.add(Pair.of(MystObjects.Blocks.decay, 1)); + //list.add(Pair.of(MystObjects.Blocks.decay, 2)); + list.add(Pair.of(MystObjects.Blocks.decay, 3)); + list.add(Pair.of(MystObjects.Blocks.decay, 4)); + //list.add(Pair.of(MystObjects.Blocks.decay, 5)); + list.add(Pair.of(MystObjects.Blocks.decay, 6)); + ItemDescriptions.DESCRIPTIONS.put(list, + new String[]{"mystcraft-jei.description.decay.1", "mystcraft-jei.description.decay.2"}); + } + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 0)), // Black decay + new String[]{"mystcraft-jei.description.decay.black.1", "mystcraft-jei.description.decay.black.2", + "mystcraft-jei.description.decay.black.3", "mystcraft-jei.description.decay.black.4"}); + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 1)), // Red decay + new String[]{"mystcraft-jei.description.decay.red.1", "mystcraft-jei.description.decay.red.2"}); + /* + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 2)), // Green decay + new String[]{"mystcraft-jei.description.decay.green.1"}); + */ + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 3)), // Blue decay + new String[]{"mystcraft-jei.description.decay.blue.1", "mystcraft-jei.description.decay.blue.2"}); + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 4)), // Purple decay + new String[]{"mystcraft-jei.description.decay.purple.1", "mystcraft-jei.description.decay.purple.2"}); + /* + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 5)), // Yellow decay + new String[]{"mystcraft-jei.description.decay.yellow.1"}); + */ + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.decay, 6)), // White decay + new String[]{"mystcraft-jei.description.decay.white.1", "mystcraft-jei.description.decay.white.2", + "mystcraft-jei.description.decay.white.3", "mystcraft-jei.description.decay.white.4"}); + + // Lectern + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.book_lectern, 0)), + new String[]{"mystcraft-jei.description.lectern.1", "mystcraft-jei.description.lectern.2"}); + + // Bookstand + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.bookstand, 0)), + new String[]{"mystcraft-jei.description.bookstand.1", "mystcraft-jei.description.bookstand.2"}); + + // Crystal + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.crystal, 0)), + new String[]{"mystcraft-jei.description.crystal.1", "mystcraft-jei.description.crystal.2"}); + + // Book receptacle + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.crystal_receptacle, 0)), + new String[]{"mystcraft-jei.description.crystal_receptacle.1", "mystcraft-jei.description.crystal_receptacle.2", + "mystcraft-jei.description.crystal_receptacle.3"}); + + // Ink mixer + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.inkmixer, 0)), + new String[]{"mystcraft-jei.description.ink_mixer.1", "mystcraft-jei.description.ink_mixer.2"}); + + // Writing desk + backboard + { + List> list = new ArrayList<>(); + list.add(Pair.of(MystObjects.Items.writing_desk, 0)); + list.add(Pair.of(MystObjects.Items.writing_desk, 1)); + ItemDescriptions.DESCRIPTIONS.put(list, + new String[]{"mystcraft-jei.description.writing_desk.1", "mystcraft-jei.description.writing_desk.2", + "mystcraft-jei.description.writing_desk.3"}); + } + + // Book binder + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.bookbinder, 0)), + new String[]{"mystcraft-jei.description.book_binder.1", "mystcraft-jei.description.book_binder.2", + "mystcraft-jei.description.book_binder.3", "mystcraft-jei.description.book_binder.4"}); + + // Link modifier + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Blocks.link_modifer, 0)), + new String[]{"mystcraft-jei.description.link_modifer.1", "mystcraft-jei.description.link_modifer.2", + "mystcraft-jei.description.link_modifer.3"}); + + // Ink vial + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.inkvial, 0)), + new String[]{"mystcraft-jei.description.ink_vial.1", "mystcraft-jei.description.ink_vial.2"}); + + // Unlinked link book + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.linkbook_unlinked, 0)), + new String[]{"mystcraft-jei.description.unlinked_linkbook.1", "mystcraft-jei.description.unlinked_linkbook.2"}); + + // Linking book + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.linkbook, 0)), + new String[]{"mystcraft-jei.description.linked_linkbook.1", "mystcraft-jei.description.linked_linkbook.2"}); + + // Sealed notebook + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.booster, 0)), + new String[]{"mystcraft-jei.description.sealed_notebook.1", "mystcraft-jei.description.sealed_notebook.2"}); + + // Collation folder + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.folder, 0)), + new String[]{"mystcraft-jei.description.folder.1", "mystcraft-jei.description.folder.2"}); + + // Symbol portfolio + ItemDescriptions.DESCRIPTIONS.put(Collections.singletonList(Pair.of(MystObjects.Items.portfolio, 0)), + new String[]{"mystcraft-jei.description.portfolio.1", "mystcraft-jei.description.portfolio.2"}); + } + + public static void setDescriptions(@Nonnull IModRegistry registry) { + ItemDescriptions.DESCRIPTIONS.forEach((stacks, descriptions) -> { + // Basic items/blocks + registry.addIngredientInfo(stacks.stream() + .map((stack) -> Pair.of(Integration.getMystItem(stack.getLeft()), stack.getRight())) + .filter((stack) -> stack.getLeft() != null && stack.getLeft() != Items.AIR) + .map((stack) -> new ItemStack(stack.getLeft(), 1, stack.getRight())) + .collect(Collectors.toList()), ItemStack.class, descriptions); + + // Link panels + // Symbol pages + }); + } + + private ItemDescriptions() { + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/JEIPlugin.java b/src/main/java/me/heldplayer/mystcraft_jei/JEIPlugin.java new file mode 100644 index 0000000..ae18c35 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/JEIPlugin.java @@ -0,0 +1,172 @@ +package me.heldplayer.mystcraft_jei; + +import com.xcompwiz.mystcraft.api.MystObjects; +import me.heldplayer.mystcraft_jei.crafting.*; +import me.heldplayer.mystcraft_jei.subtypes.PageSubtypeInterpreter; +import me.heldplayer.mystcraft_jei.subtypes.UnlinkedLinkbookInterpreter; +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.*; +import mezz.jei.api.ingredients.IIngredientBlacklist; +import mezz.jei.api.ingredients.IIngredientRegistry; +import mezz.jei.api.recipe.IRecipeCategoryRegistration; +import mezz.jei.api.recipe.VanillaRecipeCategoryUid; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +@mezz.jei.api.JEIPlugin +public class JEIPlugin extends BlankModPlugin { + private IIngredientRegistry ingredientRegistry; + + @Override + public void registerItemSubtypes(@Nonnull ISubtypeRegistry registry) { + Item page = Integration.getMystItem(MystObjects.Items.page); + if (page != null) { + registry.registerSubtypeInterpreter(page, PageSubtypeInterpreter.INSTANCE); + } + + Item unlinkedLinkbook = Integration.getMystItem(MystObjects.Items.linkbook_unlinked); + if (unlinkedLinkbook != null) { + registry.registerSubtypeInterpreter(unlinkedLinkbook, UnlinkedLinkbookInterpreter.INSTANCE); + } + + Item portfolio = Integration.getMystItem(MystObjects.Items.portfolio); + if (portfolio != null) { + registry.useNbtForSubtypes(portfolio); + } + + Item folder = Integration.getMystItem(MystObjects.Items.folder); + if (folder != null) { + registry.useNbtForSubtypes(folder); + } + } + + @Override + public void registerCategories(@Nonnull IRecipeCategoryRegistration registration) { + IGuiHelper guiHelper = registration.getJeiHelpers().getGuiHelper(); + registration.addRecipeCategories( + new WritingDeskRecipeCategory(guiHelper), + new InkMixerRecipeCategory(guiHelper), + new BookBinderRecipeCategory(guiHelper) + ); + } + + @SuppressWarnings("unchecked") + @Override + public void register(@Nonnull IModRegistry registry) { + this.ingredientRegistry = registry.getIngredientRegistry(); + + IIngredientBlacklist ingredientBlacklist = registry.getJeiHelpers().getIngredientBlacklist(); + + // Hide technical blocks + Item starFissure = Integration.getMystItem(MystObjects.Blocks.star_fissure); + if (starFissure != null) { + ingredientBlacklist.addIngredientToBlacklist(new ItemStack(starFissure)); + } + Item linkPortal = Integration.getMystItem(MystObjects.Blocks.portal); + if (linkPortal != null) { + ingredientBlacklist.addIngredientToBlacklist(new ItemStack(linkPortal)); + } + + // Hide descriptive book + Item descriptiveBook = Integration.getMystItem(MystObjects.Items.descriptive_book); + if (descriptiveBook != null) { + ingredientBlacklist.addIngredientToBlacklist(new ItemStack(descriptiveBook)); + } + + // Hide empty page + Item page = Integration.getMystItem(MystObjects.Items.page); + if (page != null) { + ingredientBlacklist.addIngredientToBlacklist(new ItemStack(page)); + } + + // Handle linking book recipe + try { + Class recipeLinkingbookClass = (Class) Class.forName("com.xcompwiz.mystcraft.data.RecipeLinkingbook"); + registry.handleRecipes(recipeLinkingbookClass, recipe -> new LinkingBookRecipeWrapper(), VanillaRecipeCategoryUid.CRAFTING); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + // Book binder recipes + Item bookBinder = Integration.getMystItem(MystObjects.Blocks.bookbinder); + if (bookBinder != null) { + registry.addRecipeCatalyst(new ItemStack(bookBinder), MystcraftRecipeCategoryUid.BOOK_BINDER); + } + registry.addRecipes(BookBinderRecipes.getRecipes(), MystcraftRecipeCategoryUid.BOOK_BINDER); + + // Writing desk recipes + Item writingDesk = Integration.getMystItem(MystObjects.Items.writing_desk); + if (writingDesk != null) { + registry.addRecipeCatalyst(new ItemStack(writingDesk, 1, 0), MystcraftRecipeCategoryUid.WRITING_DESK); + registry.addRecipeCatalyst(new ItemStack(writingDesk, 1, 1), MystcraftRecipeCategoryUid.WRITING_DESK); + } + registry.addRecipes(WritingDeskRecipes.getRecipes(registry), MystcraftRecipeCategoryUid.WRITING_DESK); + + // Ink mixer recipes + Item inkMixer = Integration.getMystItem(MystObjects.Blocks.inkmixer); + if (inkMixer != null) { + registry.addRecipeCatalyst(new ItemStack(inkMixer), MystcraftRecipeCategoryUid.INK_MIXER); + } + registry.addRecipes(InkMixerRecipes.getRecipes(registry), MystcraftRecipeCategoryUid.INK_MIXER); + + ItemDescriptions.initDescriptions(); + ItemDescriptions.setDescriptions(registry); + + IGuiHelper guiHelper = registry.getJeiHelpers().getGuiHelper(); + + MystcraftJEI.proxy.setRecipeIcon(guiHelper.createDrawable(Assets.ICONS_EXTRA, 0, 16, 16, 16, Assets.ICONS_EXTRA_WIDTH, Assets.ICONS_EXTRA_HEIGHT)); + + if (FMLCommonHandler.instance().getSide() == Side.CLIENT) { + this.addRecipeClickAreas(registry); + } + } + + @SideOnly(Side.CLIENT) + @SuppressWarnings("unchecked") + private void addRecipeClickAreas(IModRegistry registry) { + try { + registry.addRecipeClickArea((Class) Class.forName("com.xcompwiz.mystcraft.client.gui.GuiBookBinder"), 151, 8, 17, 17, MystcraftRecipeCategoryUid.BOOK_BINDER); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + try { + registry.addRecipeClickArea((Class) Class.forName("com.xcompwiz.mystcraft.client.gui.GuiWritingDesk"), 156 + 228, 45, 18, 34, MystcraftRecipeCategoryUid.WRITING_DESK); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + try { + registry.addRecipeClickArea((Class) Class.forName("com.xcompwiz.mystcraft.client.gui.GuiInkMixer"), 32, 36, 18, 18, MystcraftRecipeCategoryUid.INK_MIXER); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + @Override + public void onRuntimeAvailable(@Nonnull IJeiRuntime runtime) { + // Add decay variants + Item decay = Integration.getMystItem(MystObjects.Blocks.decay); + if (decay != null && this.ingredientRegistry != null) { + List variants = new ArrayList<>(); + variants.add(new ItemStack(decay, 1, 1)); + variants.add(new ItemStack(decay, 1, 3)); + variants.add(new ItemStack(decay, 1, 4)); + variants.add(new ItemStack(decay, 1, 6)); + this.ingredientRegistry.addIngredientsAtRuntime(ItemStack.class, variants); + } + + // Add unlinked linking books + Item unlinkedLinkbook = Integration.getMystItem(MystObjects.Items.linkbook_unlinked); + if (unlinkedLinkbook != null) { + this.ingredientRegistry.addIngredientsAtRuntime(ItemStack.class, Integration.getAllLinkbooks()); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/MystcraftJEI.java b/src/main/java/me/heldplayer/mystcraft_jei/MystcraftJEI.java new file mode 100644 index 0000000..4ee69b3 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/MystcraftJEI.java @@ -0,0 +1,31 @@ +package me.heldplayer.mystcraft_jei; + +import me.heldplayer.mystcraft_jei.util.Integration; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLInterModComms; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +@Mod(modid = Objects.MOD_ID, name = Objects.MOD_NAME, dependencies = Objects.MOD_DEPENDENCIES) +public class MystcraftJEI { + + @Mod.Instance(value = Objects.MOD_ID) + public static MystcraftJEI instance; + + @SidedProxy(clientSide = Objects.CLIENT_PROXY, serverSide = Objects.SERVER_PROXY) + public static CommonProxy proxy; + + @Mod.EventHandler + public void preInit(FMLPreInitializationEvent event) { + Objects.log = event.getModLog(); + + Integration.initialize(); + proxy.registerEventHandlers(); + } + + @Mod.EventHandler + public void init(FMLInitializationEvent event) { + FMLInterModComms.sendMessage("mystcraft", "API", "me.heldplayer.mystcraft_jei.integration.mystcraft.MystcraftIntegration.register"); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/Objects.java b/src/main/java/me/heldplayer/mystcraft_jei/Objects.java new file mode 100644 index 0000000..45f8d50 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/Objects.java @@ -0,0 +1,21 @@ +package me.heldplayer.mystcraft_jei; + +import org.apache.logging.log4j.Logger; + +/** + * MystNEIPlugin mod Objects + * + * @author heldplayer + */ +public final class Objects { + public static final String MOD_ID = "mystcraft-jei-plugin"; + public static final String MOD_NAME = "Mystcraft JEI Plugin"; + // Dependencies: https://github.com/MinecraftForge/MinecraftForge/blob/master/fml/src/main/java/net/minecraftforge/fml/common/versioning/VersionRange.java#L100 + public static final String MOD_DEPENDENCIES = "after:*;" + + "required-after:jei;" + + "required-after:mystcraft@[0.13.0,);"; + + public static final String CLIENT_PROXY = "me.heldplayer.mystcraft_jei.client.ClientProxy"; + public static final String SERVER_PROXY = "me.heldplayer.mystcraft_jei.CommonProxy"; + public static Logger log; +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/Util.java b/src/main/java/me/heldplayer/mystcraft_jei/Util.java new file mode 100644 index 0000000..fffa1a0 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/Util.java @@ -0,0 +1,14 @@ +package me.heldplayer.mystcraft_jei; + +import net.minecraft.util.text.translation.I18n; + +public class Util { + + @SuppressWarnings("deprecation") + public static String translate(String key) { + if (I18n.canTranslate(key)) { + return I18n.translateToLocal(key); + } + return I18n.translateToFallback(key); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/ClientProxy.java b/src/main/java/me/heldplayer/mystcraft_jei/client/ClientProxy.java new file mode 100644 index 0000000..c3c718e --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/ClientProxy.java @@ -0,0 +1,51 @@ +package me.heldplayer.mystcraft_jei.client; + +import me.heldplayer.mystcraft_jei.CommonProxy; +import me.heldplayer.mystcraft_jei.client.event.GuiEventHandler; +import me.heldplayer.mystcraft_jei.client.gui.*; +import me.heldplayer.mystcraft_jei.crafting.InkMixerRecipeWrapper; +import me.heldplayer.mystcraft_jei.util.Entry; +import mezz.jei.api.gui.IDrawable; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@Entry +@SideOnly(Side.CLIENT) +public class ClientProxy extends CommonProxy { + private final GuiEventHandler guiEventHandler = new GuiEventHandler(); + + @Override + public void registerEventHandlers() { + super.registerEventHandlers(); + + MinecraftForge.EVENT_BUS.register(this.guiEventHandler); + } + + @Override + @Nonnull + public IDrawableTextfield createTextField(int width, int height) { + return new DrawableTextField(FMLClientHandler.instance().getClient().fontRendererObj, width, height); + } + + @Override + @Nonnull + public IDrawable createInkEyeOverlay(@Nonnull IDrawable other, @Nonnull InkMixerRecipeWrapper recipe) { + return new DrawableInkEyeOverlay(other, recipe); + } + + @Override + @Nonnull + public IDrawable createTankBackground(int width, int height) { + return new DrawableTankBackground(width, height); + } + + @Nonnull + @Override + public IDrawable createPageList(int width, int height) { + return new DrawablePageList(width, height); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/event/GuiEventHandler.java b/src/main/java/me/heldplayer/mystcraft_jei/client/event/GuiEventHandler.java new file mode 100644 index 0000000..edbd97a --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/event/GuiEventHandler.java @@ -0,0 +1,74 @@ +package me.heldplayer.mystcraft_jei.client.event; + +import me.heldplayer.mystcraft_jei.MystcraftJEI; +import me.heldplayer.mystcraft_jei.client.gui.DrawableRecipesButton; +import net.minecraft.client.gui.GuiLabel; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; +import java.lang.reflect.Field; +import java.util.List; + +@SideOnly(Side.CLIENT) +public class GuiEventHandler { + private final Class INK_MIXER; + private final Class BOOK_BINDER; + + @SuppressWarnings("unchecked") + public GuiEventHandler() { + Class clazz = null; + try { + clazz = (Class) Class.forName("com.xcompwiz.mystcraft.client.gui.GuiInkMixer"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + this.INK_MIXER = clazz; + + clazz = null; + try { + clazz = (Class) Class.forName("com.xcompwiz.mystcraft.client.gui.GuiBookBinder"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + this.BOOK_BINDER = clazz; + } + + @SubscribeEvent + public void onGuiScreenInitGuiPost(@Nonnull GuiScreenEvent.InitGuiEvent.Post event) { + if (event.getGui() != null) { + if (this.INK_MIXER != null && this.INK_MIXER.isAssignableFrom(event.getGui().getClass())) { + GuiContainer gui = (GuiContainer) event.getGui(); + GuiHelper.addRecipesButton(gui, 33, 37); + } else if (this.BOOK_BINDER != null && this.BOOK_BINDER.isAssignableFrom(event.getGui().getClass())) { + GuiContainer gui = (GuiContainer) event.getGui(); + GuiHelper.addRecipesButton(gui, 152, 9); + } + } + } + + @SideOnly(Side.CLIENT) + private static class GuiHelper { + private static final Field LABEL_LIST; + + static { + LABEL_LIST = ReflectionHelper.findField(GuiScreen.class, "field_146293_o", "labelList"); + } + + @SuppressWarnings("unchecked") + public static void addRecipesButton(@Nonnull GuiContainer screen, int posX, int posY) { + try { + List labelList = (List) LABEL_LIST.get(screen); + labelList.removeIf(label -> label instanceof DrawableRecipesButton); + labelList.add(new DrawableRecipesButton(screen.mc.fontRendererObj, MystcraftJEI.proxy.getRecipeIcon(), -99, screen.getGuiLeft() + posX, screen.getGuiTop() + posY)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableInkEyeOverlay.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableInkEyeOverlay.java new file mode 100644 index 0000000..bbdfe31 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableInkEyeOverlay.java @@ -0,0 +1,66 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import com.xcompwiz.mystcraft.api.util.Color; +import me.heldplayer.mystcraft_jei.crafting.InkMixerRecipeWrapper; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystRender; +import mezz.jei.api.gui.IDrawable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@SideOnly(Side.CLIENT) +public class DrawableInkEyeOverlay extends Gui implements IDrawable { + + private final IDrawable other; + private final InkMixerRecipeWrapper recipe; + + public DrawableInkEyeOverlay(IDrawable other, InkMixerRecipeWrapper recipe) { + this.other = other; + this.recipe = recipe; + } + + @Override + public int getWidth() { + return this.other.getWidth(); + } + + @Override + public int getHeight() { + return this.other.getHeight(); + } + + @Override + public void draw(@Nonnull Minecraft minecraft) { + Color color = this.recipe.getCurrentEffectColor(); + if (color != null) { + int iColor = color.asInt(); + this.drawGradientRect(0, 0, this.getWidth(), this.getHeight(), 0x40000000 | iColor, 0xB0000000 | iColor); + + MystRender.drawColorEye(82.5F, 37.5F, 0.0F, 20.0F, color); + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + + this.other.draw(minecraft); + } + + @Override + public void draw(@Nonnull Minecraft minecraft, int xOffset, int yOffset) { + Color color = this.recipe.getCurrentEffectColor(); + if (color != null) { + int iColor = color.asInt(); + this.drawGradientRect(xOffset, yOffset, xOffset + this.getWidth(), yOffset + this.getHeight(), 0x40000000 | iColor, 0xB0000000 | iColor); + + MystRender.drawColorEye(xOffset + this.getWidth() / 2.0F + 0.5F, yOffset + this.getHeight() / 2.0F, 0.0F, 20.0F, color); + + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + + this.other.draw(minecraft, xOffset, yOffset); + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawablePageList.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawablePageList.java new file mode 100644 index 0000000..b027cab --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawablePageList.java @@ -0,0 +1,47 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import mezz.jei.api.gui.IDrawable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@SideOnly(Side.CLIENT) +public class DrawablePageList extends Gui implements IDrawable { + + private final int width; + private final int height; + + public DrawablePageList(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void draw(@Nonnull Minecraft minecraft) { + this.draw(minecraft, 0, 0); + } + + @Override + public void draw(@Nonnull Minecraft minecraft, int xOffset, int yOffset) { + this.drawGradientRect(xOffset, yOffset, xOffset + this.getWidth(), yOffset + this.getHeight(), 0xAA000000, 0xAA000000); + + PageRenderer.renderInkPanel(minecraft, xOffset + 2, yOffset + 3, 0.0F, (this.getHeight() - 6) * 3 / 4, this.getHeight() - 6); + + this.drawGradientRect(xOffset, yOffset, xOffset + this.getHeight() / 5, yOffset + this.getHeight(), 0x33000000, 0x33000000); + this.drawGradientRect(xOffset + this.getWidth() - this.getHeight() / 5, yOffset, xOffset + this.getWidth(), yOffset + this.getHeight(), 0x33000000, 0x33000000); + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableRecipesButton.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableRecipesButton.java new file mode 100644 index 0000000..604efe0 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableRecipesButton.java @@ -0,0 +1,31 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import mezz.jei.api.gui.IDrawable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiLabel; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@SideOnly(Side.CLIENT) +public class DrawableRecipesButton extends GuiLabel { + private final IDrawable icon; + + public DrawableRecipesButton(@Nonnull FontRenderer fontRenderer, @Nonnull IDrawable icon, int id, int posX, int posY) { + super(fontRenderer, id, posX, posY, icon.getWidth(), icon.getHeight(), 0); + this.icon = icon; + } + + @Override + public void drawLabel(@Nonnull Minecraft mc, int mouseX, int mouseY) { + if (this.visible) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + //this.drawGradientRect(this.x - 1, this.y - 1, this.x + this.icon.getWidth() + 1, this.y + this.icon.getHeight() + 1, 0xFFFF0000, 0xFFFF0000); + this.icon.draw(mc, this.x, this.y); + } + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTankBackground.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTankBackground.java new file mode 100644 index 0000000..4591337 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTankBackground.java @@ -0,0 +1,43 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import mezz.jei.api.gui.IDrawable; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@SideOnly(Side.CLIENT) +public class DrawableTankBackground extends Gui implements IDrawable { + + private final int width; + private final int height; + + public DrawableTankBackground(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void draw(@Nonnull Minecraft minecraft) { + this.draw(minecraft, 0, 0); + } + + @Override + public void draw(@Nonnull Minecraft minecraft, int xOffset, int yOffset) { + this.drawGradientRect(xOffset, yOffset, xOffset + this.getWidth(), yOffset + this.getHeight(), 0x99000000, 0x99000000); + this.drawGradientRect(xOffset + 1, yOffset + 1, xOffset + this.getWidth() - 1, yOffset + this.getHeight() - 1, 0xFFCCCCEE, 0xFF666699); + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTextField.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTextField.java new file mode 100644 index 0000000..ef071c5 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/DrawableTextField.java @@ -0,0 +1,54 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiTextField; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +@SideOnly(Side.CLIENT) +public class DrawableTextField extends Gui implements IDrawableTextfield { + + private final int width; + private final int height; + private final GuiTextField textField; + + public DrawableTextField(@Nonnull FontRenderer fontRenderer, int width, int height) { + this.width = width; + this.height = height; + this.textField = new GuiTextField(0, fontRenderer, 0, 0, width, height); + } + + @Override + public void setText(@Nonnull String text, boolean enabled) { + this.textField.setText(text); + this.textField.setCursorPosition(0); + this.textField.setEnabled(enabled); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public void draw(@Nonnull Minecraft minecraft) { + this.draw(minecraft, 0, 0); + } + + @Override + public void draw(@Nonnull Minecraft minecraft, int xOffset, int yOffset) { + this.textField.xPosition = xOffset; + this.textField.yPosition = yOffset; + this.textField.drawTextBox(); + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/EmptyDrawable.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/EmptyDrawable.java new file mode 100644 index 0000000..6037aec --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/EmptyDrawable.java @@ -0,0 +1,41 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +public class EmptyDrawable implements IDrawableTextfield { + private final int width; + private final int height; + + public EmptyDrawable(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public void setText(@Nonnull String text, boolean enabled) { + } + + @Override + public int getWidth() { + return this.width; + } + + @Override + public int getHeight() { + return this.height; + } + + @Override + @SideOnly(Side.CLIENT) + public void draw(@Nonnull Minecraft minecraft) { + } + + @Override + @SideOnly(Side.CLIENT) + public void draw(@Nonnull Minecraft minecraft, int xOffset, int yOffset) { + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/IDrawableTextfield.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/IDrawableTextfield.java new file mode 100644 index 0000000..c38ab60 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/IDrawableTextfield.java @@ -0,0 +1,10 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import mezz.jei.api.gui.IDrawable; + +import javax.annotation.Nonnull; + +public interface IDrawableTextfield extends IDrawable { + + void setText(@Nonnull String text, boolean enabled); +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/client/gui/PageRenderer.java b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/PageRenderer.java new file mode 100644 index 0000000..948b633 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/client/gui/PageRenderer.java @@ -0,0 +1,79 @@ +package me.heldplayer.mystcraft_jei.client.gui; + +import me.heldplayer.mystcraft_jei.Assets; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystRender; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.VertexBuffer; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.lwjgl.opengl.GL11; + +import javax.annotation.Nonnull; + +public class PageRenderer { + private PageRenderer() { + } + + @SideOnly(Side.CLIENT) + public static void renderPage(@Nonnull Minecraft minecraft, @Nonnull String symbol, float posX, float posY, float zLevel, float width, float height) { + minecraft.getTextureManager().bindTexture(Assets.BOOK_PAGE_LEFT); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + drawTexturedModalRect(posX, posY, width, height, zLevel, 0.609375F, 0.0F, 0.7265625F, 0.15625F); + + MystRender.drawSymbol(posX + 0.5F, posY + (height + 1.0F - width) / 2.0F, 0.0F, width - 1.0F, symbol); + } + + @SideOnly(Side.CLIENT) + public static void renderInkPanel(@Nonnull Minecraft minecraft, float posX, float posY, float zLevel, float width, float height) { + minecraft.getTextureManager().bindTexture(Assets.BOOK_PAGE_LEFT); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + drawTexturedModalRect(posX, posY, width, height, zLevel, 0.609375F, 0.0F, 0.7265625F, 0.15625F); + + drawGradientRect(posX + width * 0.15F, posY + height * 0.15F, posX + width * 0.85F, posY + height * 0.5F, zLevel, 0xff000000, 0xff000000); + } + + private static void drawGradientRect(float left, float top, float right, float bottom, float zLevel, int startColor, int endColor) { + float startAlpha = (float) (startColor >> 24 & 255) / 255.0F; + float startRed = (float) (startColor >> 16 & 255) / 255.0F; + float startGreen = (float) (startColor >> 8 & 255) / 255.0F; + float startBlue = (float) (startColor & 255) / 255.0F; + float endAlpha = (float) (endColor >> 24 & 255) / 255.0F; + float endRed = (float) (endColor >> 16 & 255) / 255.0F; + float endGreen = (float) (endColor >> 8 & 255) / 255.0F; + float endBlue = (float) (endColor & 255) / 255.0F; + GlStateManager.disableTexture2D(); + GlStateManager.enableBlend(); + GlStateManager.disableAlpha(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + GlStateManager.shadeModel(GL11.GL_SMOOTH); + GL11.glShadeModel(GL11.GL_SMOOTH); + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer vertexbuffer = tessellator.getBuffer(); + vertexbuffer.begin(7, DefaultVertexFormats.POSITION_COLOR); + vertexbuffer.pos(right, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex(); + vertexbuffer.pos(left, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex(); + vertexbuffer.pos(left, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex(); + vertexbuffer.pos(right, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex(); + tessellator.draw(); + GlStateManager.shadeModel(GL11.GL_FLAT); + GlStateManager.disableBlend(); + GlStateManager.enableAlpha(); + GlStateManager.enableTexture2D(); + } + + @SideOnly(Side.CLIENT) + private static void drawTexturedModalRect(float posX, float posY, float width, float height, float zLevel, float uStart, float vStart, float uEnd, float vEnd) { + Tessellator tessellator = Tessellator.getInstance(); + VertexBuffer vertexbuffer = tessellator.getBuffer(); + vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX); + vertexbuffer.pos(posX, posY + height, zLevel).tex(uStart, vEnd).endVertex(); + vertexbuffer.pos(posX + width, posY + height, zLevel).tex(uEnd, vEnd).endVertex(); + vertexbuffer.pos(posX + width, posY, zLevel).tex(uEnd, vStart).endVertex(); + vertexbuffer.pos(posX, posY, zLevel).tex(uStart, vStart).endVertex(); + tessellator.draw(); + } +} + diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipeCategory.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipeCategory.java new file mode 100644 index 0000000..7161054 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipeCategory.java @@ -0,0 +1,66 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import me.heldplayer.mystcraft_jei.MystcraftJEI; +import me.heldplayer.mystcraft_jei.Util; +import me.heldplayer.mystcraft_jei.client.gui.IDrawableTextfield; +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeCategory; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; + +public class BookBinderRecipeCategory extends BlankRecipeCategory { + public static final int SLOT_LEATHER = 0; + public static final int SLOT_RESULT = 1; + + private final IDrawable background; + private final String title; + + private final IDrawableTextfield textField; + + public BookBinderRecipeCategory(@Nonnull IGuiHelper guiHelper) { + ResourceLocation location = new ResourceLocation("mystcraft", "gui/pagebinder.png"); + this.background = guiHelper.createDrawable(location, 7, 7, 162, 70); + this.title = Util.translate("mystcraft-jei.category.book_binder"); + this.textField = MystcraftJEI.proxy.createTextField(114, 12); + } + + @Override + @Nonnull + public String getUid() { + return MystcraftRecipeCategoryUid.BOOK_BINDER; + } + + @Override + @Nonnull + public String getTitle() { + return this.title; + } + + @Override + @Nonnull + public String getModName() { + return "Mystcraft"; + } + + @Override + @Nonnull + public IDrawable getBackground() { + return this.background; + } + + @Override + public void setRecipe(@Nonnull IRecipeLayout recipeLayout, @Nonnull BookBinderRecipes.AwareRecipeWrapper wrapper, @Nonnull IIngredients ingredients) { + IGuiItemStackGroup guiItemStacks = recipeLayout.getItemStacks(); + + guiItemStacks.init(SLOT_LEATHER, true, 0, 19); + guiItemStacks.init(SLOT_RESULT, false, 144, 19); + guiItemStacks.set(ingredients); + + wrapper.setTextField(this.textField); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipes.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipes.java new file mode 100644 index 0000000..ac64c8d --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/BookBinderRecipes.java @@ -0,0 +1,71 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import com.xcompwiz.mystcraft.api.MystObjects; +import me.heldplayer.mystcraft_jei.MystcraftJEI; +import me.heldplayer.mystcraft_jei.client.gui.IDrawableTextfield; +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class BookBinderRecipes { + + private BookBinderRecipes() { + } + + public static Collection getRecipes() { + return Collections.singletonList(new AgeWrite()); + } + + public abstract static class AwareRecipeWrapper extends BlankRecipeWrapper { + protected IDrawableTextfield textField; + + public void setTextField(IDrawableTextfield textField) { + this.textField = textField; + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + this.textField.draw(minecraft, 1, 3); + } + } + + private static class AgeWrite extends AwareRecipeWrapper { + private final IDrawable pageList = MystcraftJEI.proxy.createPageList(162, 40); + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + List> inputStacks = new ArrayList<>(); + inputStacks.add(Collections.singletonList(new ItemStack(Items.LEATHER))); + + // Extra input stacks that are not shown in a slot, but are in the "recipe" + inputStacks.add(Integration.gatherAllSymbolStacks()); + + List> outputStacks = new ArrayList<>(); + Item descriptiveBook = Integration.getMystItem(MystObjects.Items.descriptive_book); + outputStacks.add(descriptiveBook == null ? Collections.emptyList() : Collections.singletonList(new ItemStack(descriptiveBook))); + + ingredients.setInputLists(ItemStack.class, inputStacks); + ingredients.setOutputLists(ItemStack.class, outputStacks); + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + this.textField.setText("Age Name", true); + + super.drawInfo(minecraft, recipeWidth, recipeHeight, mouseX, mouseY); + + this.pageList.draw(minecraft, 0, 38); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeCategory.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeCategory.java new file mode 100644 index 0000000..9d27b93 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeCategory.java @@ -0,0 +1,95 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import me.heldplayer.mystcraft_jei.Assets; +import me.heldplayer.mystcraft_jei.MystcraftJEI; +import me.heldplayer.mystcraft_jei.Util; +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiFluidStackGroup; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeCategory; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; + +public class InkMixerRecipeCategory extends BlankRecipeCategory { + public static final int SLOT_TANK_INPUT = 0; + public static final int SLOT_TANK_OUTPUT = 1; + public static final int SLOT_PAPER = 2; + public static final int SLOT_RESULT = 3; + public static final int SLOT_INGREDIENT = 4; + public static final int TANK_INK = 0; + + public static final int width = 166; + public static final int height = 76; + + private final IDrawable background; + private final IDrawable tankOverlay; + private final IDrawable iconDrop; + private final String title; + + public InkMixerRecipeCategory(@Nonnull IGuiHelper guiHelper) { + ResourceLocation location = new ResourceLocation("mystcraft", "gui/inkmixer.png"); + this.background = guiHelper.createDrawable(location, 5, 11, width, height); + this.tankOverlay = guiHelper.createDrawable(location, 54, 16, 67, 66); + this.iconDrop = guiHelper.createDrawable(Assets.ICONS_EXTRA, 0, 0, 18, 10, Assets.ICONS_EXTRA_WIDTH, Assets.ICONS_EXTRA_HEIGHT); + this.title = Util.translate("mystcraft-jei.category.ink_mixer"); + } + + @Override + @Nonnull + public String getUid() { + return MystcraftRecipeCategoryUid.INK_MIXER; + } + + @Override + @Nonnull + public String getTitle() { + return this.title; + } + + @Override + @Nonnull + public String getModName() { + return "Mystcraft"; + } + + @Override + @Nonnull + public IDrawable getBackground() { + return this.background; + } + + @Override + public void setRecipe(@Nonnull IRecipeLayout recipeLayout, @Nonnull IRecipeWrapper wrapper, @Nonnull IIngredients ingredients) { + IGuiItemStackGroup guiItemStacks = recipeLayout.getItemStacks(); + IGuiFluidStackGroup guiFluidStacks = recipeLayout.getFluidStacks(); + + guiItemStacks.init(SLOT_TANK_INPUT, true, 2, 15); + guiItemStacks.init(SLOT_TANK_OUTPUT, false, 146, 15); + guiItemStacks.init(SLOT_PAPER, true, 2, 36); + guiItemStacks.init(SLOT_RESULT, false, 146, 36); + guiItemStacks.init(SLOT_INGREDIENT, true, 27, 25); + guiItemStacks.set(ingredients); + + IDrawable tankOverlay = this.tankOverlay; + if (wrapper instanceof InkMixerRecipeWrapper) { + InkMixerRecipeWrapper recipe = (InkMixerRecipeWrapper) wrapper; + tankOverlay = MystcraftJEI.proxy.createInkEyeOverlay(tankOverlay, recipe); + recipe.setDropIcon(this.iconDrop); + } + + if (ingredients.getInputs(FluidStack.class).size() > 0) { + guiFluidStacks.init(TANK_INK, true, 49, 5, 67, 66, 1000, false, tankOverlay); + } + if (ingredients.getOutputs(FluidStack.class).size() > 0) { + guiFluidStacks.init(TANK_INK, false, 49, 5, 67, 66, 1000, false, tankOverlay); + } + + guiFluidStacks.set(ingredients); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeWrapper.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeWrapper.java new file mode 100644 index 0000000..bef047f --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipeWrapper.java @@ -0,0 +1,138 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import com.xcompwiz.mystcraft.api.MystObjects; +import com.xcompwiz.mystcraft.api.util.Color; +import com.xcompwiz.mystcraft.api.util.ColorGradient; +import me.heldplayer.mystcraft_jei.InkMixerOutput; +import me.heldplayer.mystcraft_jei.Util; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystItemFactory; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.OreDictionary; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class InkMixerRecipeWrapper extends BlankRecipeWrapper { + + @Nonnull + private final List ingredients; + @Nonnull + private final String[] modifiers; + @Nullable + private final ColorGradient gradient; + + private IDrawable iconDrop; + + private int frame; + + public InkMixerRecipeWrapper(@Nonnull ItemStack stack) { + this.ingredients = Collections.singletonList(stack); + + InkMixerOutput result = InkMixerOutput.getOutputFor(stack); + if (result == null) { + this.modifiers = new String[0]; + this.gradient = null; + } else { + this.modifiers = result.getModifiers(); + this.gradient = result.getGradient(); + } + } + + public InkMixerRecipeWrapper(@Nonnull String oredict) { + this.ingredients = OreDictionary.getOres(oredict); + + InkMixerOutput result = this.ingredients.size() > 0 ? InkMixerOutput.getOutputFor(this.ingredients.get(0)) : null; + if (result == null) { + this.modifiers = new String[0]; + this.gradient = null; + } else { + this.modifiers = result.getModifiers(); + this.gradient = result.getGradient(); + } + } + + public InkMixerRecipeWrapper(@Nonnull Item item) { + this.ingredients = Collections.singletonList(new ItemStack(item)); + + InkMixerOutput result = InkMixerOutput.getOutputFor(this.ingredients.get(0)); + if (result == null) { + this.modifiers = new String[0]; + this.gradient = null; + } else { + this.modifiers = result.getModifiers(); + this.gradient = result.getGradient(); + } + } + + public boolean notEmpty() { + return !this.ingredients.isEmpty(); + } + + public void setDropIcon(IDrawable icon) { + this.iconDrop = icon; + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + if (!GuiScreen.isShiftKeyDown()) { + this.frame++; + } + + if (this.iconDrop != null) { + this.iconDrop.draw(minecraft, 27, 45); + } + } + + @Nullable + public Color getCurrentEffectColor() { + return this.gradient == null ? null : this.gradient.getColor((float) this.frame / 300.0F); + } + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + List> inputStacks = new ArrayList<>(); + inputStacks.add(Collections.emptyList()); + inputStacks.add(Collections.singletonList(new ItemStack(Items.PAPER))); + inputStacks.add(this.ingredients); + + List> outputStacks = new ArrayList<>(); + outputStacks.add(Collections.emptyList()); + outputStacks.add(Collections.singletonList(MystItemFactory.buildLinkPage(this.modifiers))); + // Extra output stacks that are not shown, for when there's more than one modifier on an item + if (this.modifiers.length > 1) { + outputStacks.add(Arrays.stream(this.modifiers).map(MystItemFactory::buildLinkPage).collect(Collectors.toList())); + } + + List> inputFluids = new ArrayList<>(); + inputFluids.add(Collections.singletonList(FluidRegistry.getFluidStack(MystObjects.Fluids.black_ink, 1000))); + + ingredients.setInputLists(ItemStack.class, inputStacks); + ingredients.setInputLists(FluidStack.class, inputFluids); + ingredients.setOutputLists(ItemStack.class, outputStacks); + } + + @Override + @Nonnull + public List getTooltipStrings(int mouseX, int mouseY) { + // Draw tooltip for dropping item in the puddle + if (mouseX >= 27 && mouseX < 45 && mouseY >= 45 && mouseY < 55) { + return Collections.singletonList(Util.translate("mystcraft-jei.ink_mixer.drop")); + } + + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipes.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipes.java new file mode 100644 index 0000000..4fe5a66 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/InkMixerRecipes.java @@ -0,0 +1,80 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.IModRegistry; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import mezz.jei.api.recipe.IRecipeWrapper; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class InkMixerRecipes { + private InkMixerRecipes() { + } + + public static Collection getRecipes(@Nonnull IModRegistry modRegistry) { + List inputs = Integration.getInkMixerInputs(); + Set validInks = Integration.getValidInks(); + + return Stream.concat( + modRegistry.getIngredientRegistry().getIngredients(ItemStack.class).stream() + .map((stack) -> Pair.of(stack, FluidUtil.getFluidContained(stack))) + .filter((stacks) -> stacks.getRight() != null) + .filter((stacks) -> stacks.getRight().amount == 1000) + .filter((stacks) -> validInks.contains(stacks.getRight().getFluid().getName())) + .map((stacks) -> new InkFill(stacks.getLeft(), stacks.getRight())), + + inputs.stream() + .map(input -> { + if (input instanceof ItemStack) { + return new InkMixerRecipeWrapper((ItemStack) input); + } else if (input instanceof String) { + return new InkMixerRecipeWrapper((String) input); + } else if (input instanceof Item) { + return new InkMixerRecipeWrapper((Item) input); + } + return null; + }) + .filter(Objects::nonNull) + .filter(InkMixerRecipeWrapper::notEmpty) + ).collect(Collectors.toList()); + } + + private static class InkFill extends BlankRecipeWrapper { + + private final ItemStack inputItem; + private final ItemStack outputItem; + private final FluidStack fluid; + + public InkFill(@Nonnull ItemStack input, @Nonnull FluidStack fluid) { + this.inputItem = input.copy(); + this.outputItem = input.getItem().getContainerItem(input); + + this.fluid = fluid.copy(); + } + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + List> inputStacks = new ArrayList<>(); + inputStacks.add(Collections.singletonList(this.inputItem)); + + List> outputStacks = new ArrayList<>(); + outputStacks.add(this.outputItem.isEmpty() ? Collections.emptyList() : Collections.singletonList(this.outputItem)); + + List> outputFluids = new ArrayList<>(); + outputFluids.add(Collections.singletonList(this.fluid)); + + ingredients.setInputLists(ItemStack.class, inputStacks); + ingredients.setOutputLists(FluidStack.class, outputFluids); + ingredients.setOutputLists(ItemStack.class, outputStacks); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/LinkingBookRecipeWrapper.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/LinkingBookRecipeWrapper.java new file mode 100644 index 0000000..06a9237 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/LinkingBookRecipeWrapper.java @@ -0,0 +1,32 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import com.google.common.collect.Lists; +import com.xcompwiz.mystcraft.api.MystObjects; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystItemFactory; +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import javax.annotation.Nonnull; + +public class LinkingBookRecipeWrapper extends BlankRecipeWrapper { + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + Item result = Integration.getMystItem(MystObjects.Items.linkbook_unlinked); + if (result == null) { + return; + } + ItemStack pageStack = MystItemFactory.buildLinkPage(); + ItemStack resultStack = new ItemStack(result); + NBTTagCompound tag = pageStack.getTagCompound(); + resultStack.setTagCompound(tag == null ? null : tag.copy()); + + ingredients.setInputs(ItemStack.class, Lists.newArrayList(new ItemStack(Items.LEATHER), pageStack)); + ingredients.setOutput(ItemStack.class, resultStack); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/MystcraftRecipeCategoryUid.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/MystcraftRecipeCategoryUid.java new file mode 100644 index 0000000..77e5b59 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/MystcraftRecipeCategoryUid.java @@ -0,0 +1,11 @@ +package me.heldplayer.mystcraft_jei.crafting; + +public final class MystcraftRecipeCategoryUid { + + public static final String BOOK_BINDER = "mystcraft.book_binder"; + public static final String INK_MIXER = "mystcraft.ink_mixer"; + public static final String WRITING_DESK = "mystcraft.writing_desk"; + + private MystcraftRecipeCategoryUid() { + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipeCategory.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipeCategory.java new file mode 100644 index 0000000..c79e7a6 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipeCategory.java @@ -0,0 +1,95 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import me.heldplayer.mystcraft_jei.MystcraftJEI; +import me.heldplayer.mystcraft_jei.Util; +import me.heldplayer.mystcraft_jei.client.gui.IDrawableTextfield; +import mezz.jei.api.IGuiHelper; +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IGuiFluidStackGroup; +import mezz.jei.api.gui.IGuiItemStackGroup; +import mezz.jei.api.gui.IRecipeLayout; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeCategory; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; + +public class WritingDeskRecipeCategory extends BlankRecipeCategory { + public static final int SLOT_PAPER = 0; + public static final int SLOT_TANK_INPUT = 1; + public static final int SLOT_RESULT = 2; + public static final int SLOT_TANK_OUTPUT = 3; + public static final int TANK_INK = 0; + + public static final int width = 162; + public static final int height = 70; + + private final IDrawable background; + private final String title; + + private final IDrawable tankBackground; + private final IDrawableTextfield textField; + + public WritingDeskRecipeCategory(@Nonnull IGuiHelper guiHelper) { + ResourceLocation location = new ResourceLocation("mystcraft", "gui/writingdesk.png"); + this.background = guiHelper.createDrawable(location, 7, 7, width, height); + this.title = Util.translate("mystcraft-jei.category.writing_desk"); + this.tankBackground = MystcraftJEI.proxy.createTankBackground(16, 70); + this.textField = MystcraftJEI.proxy.createTextField(97, 12); + } + + @Override + @Nonnull + public String getUid() { + return MystcraftRecipeCategoryUid.WRITING_DESK; + } + + @Override + @Nonnull + public String getTitle() { + return this.title; + } + + @Override + @Nonnull + public String getModName() { + return "Mystcraft"; + } + + @Override + @Nonnull + public IDrawable getBackground() { + return this.background; + } + + @Override + public void setRecipe(@Nonnull IRecipeLayout recipeLayout, @Nonnull WritingDeskRecipes.AwareRecipeWrapper wrapper, @Nonnull IIngredients ingredients) { + IGuiItemStackGroup guiItemStacks = recipeLayout.getItemStacks(); + IGuiFluidStackGroup guiFluidStacks = recipeLayout.getFluidStacks(); + + guiItemStacks.init(SLOT_PAPER, true, 0, 0); + guiItemStacks.init(SLOT_TANK_INPUT, true, 144, 0); + guiItemStacks.init(SLOT_RESULT, false, 0, 52); + guiItemStacks.init(SLOT_TANK_OUTPUT, false, 144, 52); + guiItemStacks.set(ingredients); + + if (ingredients.getInputs(FluidStack.class).size() > 0) { + guiFluidStacks.init(TANK_INK, true, 126, 1, 14, 68, 1000, false, null); + } + if (ingredients.getOutputs(FluidStack.class).size() > 0) { + guiFluidStacks.init(TANK_INK, false, 126, 1, 14, 68, 1000, false, null); + } + + guiFluidStacks.set(ingredients); + + wrapper.setInputSlot(guiItemStacks.getGuiIngredients().get(SLOT_RESULT)); + wrapper.setTextField(this.textField); + } + + @Override + public void drawExtras(@Nonnull Minecraft minecraft) { + this.tankBackground.draw(minecraft, 125, 0); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipes.java b/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipes.java new file mode 100644 index 0000000..b4ab714 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/crafting/WritingDeskRecipes.java @@ -0,0 +1,184 @@ +package me.heldplayer.mystcraft_jei.crafting; + +import com.xcompwiz.mystcraft.api.symbol.IAgeSymbol; +import me.heldplayer.mystcraft_jei.client.gui.IDrawableTextfield; +import me.heldplayer.mystcraft_jei.client.gui.PageRenderer; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystPage; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystSymbol; +import me.heldplayer.mystcraft_jei.util.Integration; +import mezz.jei.api.IModRegistry; +import mezz.jei.api.gui.IGuiIngredient; +import mezz.jei.api.ingredients.IIngredients; +import mezz.jei.api.recipe.BlankRecipeWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class WritingDeskRecipes { + + private WritingDeskRecipes() { + } + + public static Collection getRecipes(@Nonnull IModRegistry modRegistry) { + Set validInks = Integration.getValidInks(); + int inkcost = Integration.getInkcost(); + + return Stream.concat( + modRegistry.getIngredientRegistry().getIngredients(ItemStack.class).stream() + .map((stack) -> Pair.of(stack, FluidUtil.getFluidContained(stack))) + .filter((stacks) -> stacks.getRight() != null) + .filter((stacks) -> stacks.getRight().amount <= 1000) + .filter((stacks) -> validInks.contains(stacks.getRight().getFluid().getName())) + .map((stacks) -> new InkFill(stacks.getLeft(), stacks.getRight())), + + Stream.of(new SymbolWrite(validInks.stream() + .map((name) -> FluidRegistry.getFluidStack(name, inkcost)) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + )) + ).collect(Collectors.toList()); + } + + public abstract static class AwareRecipeWrapper extends BlankRecipeWrapper { + protected IGuiIngredient inputSlot; + protected IDrawableTextfield textField; + + public void setInputSlot(IGuiIngredient inputSlot) { + this.inputSlot = inputSlot; + } + + public void setTextField(IDrawableTextfield textField) { + this.textField = textField; + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + this.textField.draw(minecraft, 22, 55); + } + } + + private static class InkFill extends AwareRecipeWrapper { + private final ItemStack inputItem; + private final ItemStack outputItem; + private final FluidStack fluid; + + public InkFill(@Nonnull ItemStack input, @Nonnull FluidStack fluid) { + this.inputItem = input.copy(); + this.outputItem = input.getItem().getContainerItem(input); + + this.fluid = fluid.copy(); + } + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + List> inputStacks = new ArrayList<>(); + inputStacks.add(Collections.emptyList()); + inputStacks.add(Collections.singletonList(this.inputItem)); + + List> outputStacks = new ArrayList<>(); + outputStacks.add(Collections.emptyList()); + outputStacks.add(this.outputItem.isEmpty() ? Collections.emptyList() : Collections.singletonList(this.outputItem)); + + List> outputFluids = new ArrayList<>(); + outputFluids.add(Collections.singletonList(this.fluid)); + + ingredients.setInputLists(ItemStack.class, inputStacks); + ingredients.setOutputLists(FluidStack.class, outputFluids); + ingredients.setOutputLists(ItemStack.class, outputStacks); + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + this.textField.setText("", false); + + super.drawInfo(minecraft, recipeWidth, recipeHeight, mouseX, mouseY); + } + } + + private static class SymbolWrite extends AwareRecipeWrapper { + private final List validFluids; + + private SymbolWrite(List validFluids) { + this.validFluids = validFluids; + } + + @Override + public void getIngredients(@Nonnull IIngredients ingredients) { + List> inputStacks = new ArrayList<>(); + inputStacks.add(Collections.singletonList(new ItemStack(Items.PAPER))); + + List> outputStacks = new ArrayList<>(); + outputStacks.add(Integration.gatherAllSymbolStacks()); + + List> inputFluids = new ArrayList<>(); + inputFluids.add(this.validFluids); + + ingredients.setInputLists(ItemStack.class, inputStacks); + ingredients.setInputLists(FluidStack.class, inputFluids); + ingredients.setOutputLists(ItemStack.class, outputStacks); + } + + @Override + public void drawInfo(@Nonnull Minecraft minecraft, int recipeWidth, int recipeHeight, int mouseX, int mouseY) { + this.textField.setText("", false); + + ItemStack ingredient = this.inputSlot.getDisplayedIngredient(); + if (ingredient != null) { + String symbol = MystPage.getPageSymbol(ingredient); + if (symbol != null) { + IAgeSymbol ageSymbol = MystSymbol.getSymbol(symbol); + if (ageSymbol != null) { + PageRenderer.renderPage(minecraft, symbol, 26.0F, 0.0F, 0.0F, 37.0F, 50.0F); + this.textField.setText(ageSymbol.getLocalizedName(), false); + } + } + } + + super.drawInfo(minecraft, recipeWidth, recipeHeight, mouseX, mouseY); + } + + @Override + @Nonnull + public List getTooltipStrings(int mouseX, int mouseY) { + // Draw tooltip for enlarged page preview + if (mouseX >= 26 && mouseX < 63 && mouseY >= 0 && mouseY < 50) { + ItemStack ingredient = this.inputSlot.getDisplayedIngredient(); + if (ingredient != null) { + String symbol = MystPage.getPageSymbol(ingredient); + if (symbol != null) { + IAgeSymbol ageSymbol = MystSymbol.getSymbol(symbol); + if (ageSymbol != null) { + return Collections.singletonList(ageSymbol.getLocalizedName()); + } + } + } + } + + // Draw tooltip for text field + /* + if (mouseX >= 21 && mouseX < 120 && mouseY >= 54 && mouseY < 68) { + ItemStack ingredient = this.inputSlot.getDisplayedIngredient(); + if (ingredient != null) { + String symbol = MystPage.api.getPageSymbol(ingredient); + if (symbol != null) { + IAgeSymbol ageSymbol = MystSymbol.api.getSymbol(symbol); + if (ageSymbol != null) { + return Collections.singletonList(Util.translate("mystcraft-jei.writing_desk.page.name")); + } + } + } + } + */ + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystItemFactory.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystItemFactory.java new file mode 100644 index 0000000..b08ce7b --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystItemFactory.java @@ -0,0 +1,28 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.hook.ItemFactory; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; + +public class MystItemFactory { + private static ItemFactory api; + + protected static void setAPI(@Nonnull Object api) { + MystItemFactory.api = (ItemFactory) api; + } + + public static boolean isReady() { + return api != null; + } + + @Nonnull + public static ItemStack buildSymbolPage(@Nonnull String s) { + return api.buildSymbolPage(s); + } + + @Nonnull + public static ItemStack buildLinkPage(@Nonnull String... strings) { + return api.buildLinkPage(strings); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystLinkProperty.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystLinkProperty.java new file mode 100644 index 0000000..f7f9b7f --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystLinkProperty.java @@ -0,0 +1,31 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.hook.LinkPropertyAPI; +import com.xcompwiz.mystcraft.api.util.ColorGradient; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + +public class MystLinkProperty { + private static LinkPropertyAPI api; + + protected static void setAPI(@Nonnull Object api) { + MystLinkProperty.api = (LinkPropertyAPI) api; + } + + public static boolean isReady() { + return api != null; + } + + @Nonnull + public static ColorGradient getPropertiesGradient(@Nonnull Map map) { + return api.getPropertiesGradient(map); + } + + @Nullable + public static Map getPropertiesForItem(@Nonnull ItemStack itemStack) { + return api.getPropertiesForItem(itemStack); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystPage.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystPage.java new file mode 100644 index 0000000..a1ab09a --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystPage.java @@ -0,0 +1,24 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.hook.PageAPI; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class MystPage { + private static PageAPI api; + + protected static void setAPI(@Nonnull Object api) { + MystPage.api = (PageAPI) api; + } + + public static boolean isReady() { + return api != null; + } + + @Nullable + public static String getPageSymbol(@Nonnull ItemStack itemStack) { + return api.getPageSymbol(itemStack); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystRender.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystRender.java new file mode 100644 index 0000000..b4dff48 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystRender.java @@ -0,0 +1,30 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.hook.RenderAPI; +import com.xcompwiz.mystcraft.api.util.Color; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import javax.annotation.Nonnull; + +public class MystRender { + private static RenderAPI api; + + protected static void setAPI(@Nonnull Object api) { + MystRender.api = (RenderAPI) api; + } + + public static boolean isReady() { + return api != null; + } + + @SideOnly(Side.CLIENT) + public static void drawSymbol(float posX, float posY, float zLevel, float radius, @Nonnull String identifier) { + api.drawSymbol(posX, posY, zLevel, radius, identifier); + } + + @SideOnly(Side.CLIENT) + public static void drawColorEye(float posX, float posY, float zLevel, float radius, @Nonnull Color color) { + api.drawColorEye(posX, posY, zLevel, radius, color); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystSymbol.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystSymbol.java new file mode 100644 index 0000000..e765ede --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystSymbol.java @@ -0,0 +1,30 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.hook.SymbolAPI; +import com.xcompwiz.mystcraft.api.symbol.IAgeSymbol; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class MystSymbol { + private static SymbolAPI api; + + protected static void setAPI(@Nonnull Object api) { + MystSymbol.api = (SymbolAPI) api; + } + + public static boolean isReady() { + return api != null; + } + + @Nonnull + public static List getAllRegisteredSymbols() { + return api.getAllRegisteredSymbols(); + } + + @Nullable + public static IAgeSymbol getSymbol(@Nonnull String s) { + return api.getSymbol(s); + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystcraftIntegration.java b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystcraftIntegration.java new file mode 100644 index 0000000..15ba0ef --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/integration/mystcraft/MystcraftIntegration.java @@ -0,0 +1,87 @@ +package me.heldplayer.mystcraft_jei.integration.mystcraft; + +import com.xcompwiz.mystcraft.api.APIInstanceProvider; +import com.xcompwiz.mystcraft.api.exception.APIUndefined; +import com.xcompwiz.mystcraft.api.exception.APIVersionRemoved; +import com.xcompwiz.mystcraft.api.exception.APIVersionUndefined; +import me.heldplayer.mystcraft_jei.Objects; +import me.heldplayer.mystcraft_jei.util.Entry; + +@Entry +public class MystcraftIntegration { + @Entry + public static void register(APIInstanceProvider provider) { + Objects.log.info("Received the Mystcraft API provider"); + + MystcraftIntegration.getLinkPropertyAPI(provider); + MystcraftIntegration.getSymbolAPI(provider); + MystcraftIntegration.getItemFactoryAPI(provider); + MystcraftIntegration.getRenderAPI(provider); + MystcraftIntegration.getPageAPI(provider); + } + + private static void getLinkPropertyAPI(APIInstanceProvider provider) { + try { + Object api = provider.getAPIInstance("linkingprop-1"); + MystLinkProperty.setAPI(api); + } catch (APIUndefined e) { + Objects.log.error("The Mystcraft linkingprop API is missing", e); + } catch (APIVersionUndefined e) { + Objects.log.error("Mystcraft can't count to 1, missing the linkingprop API", e); + } catch (APIVersionRemoved e) { + Objects.log.error("Version 1 of the Mystcraft linkingprop API was removed, NEI Mystcraft Plugin needs to be updated", e); + } + } + + private static void getSymbolAPI(APIInstanceProvider provider) { + try { + Object api = provider.getAPIInstance("symbol-1"); + MystSymbol.setAPI(api); + } catch (APIUndefined e) { + Objects.log.error("The Mystcraft symbol API is missing", e); + } catch (APIVersionUndefined e) { + Objects.log.error("Mystcraft can't count to 1, missing the symbol API", e); + } catch (APIVersionRemoved e) { + Objects.log.error("Version 1 of the Mystcraft symbol API was removed, NEI Mystcraft Plugin needs to be updated", e); + } + } + + private static void getItemFactoryAPI(APIInstanceProvider provider) { + try { + Object api = provider.getAPIInstance("itemfact-1"); + MystItemFactory.setAPI(api); + } catch (APIUndefined e) { + Objects.log.error("The Mystcraft itemfact API is missing", e); + } catch (APIVersionUndefined e) { + Objects.log.error("Mystcraft can't count to 1, missing the itemfact API", e); + } catch (APIVersionRemoved e) { + Objects.log.error("Version 1 of the Mystcraft itemfact API was removed, NEI Mystcraft Plugin needs to be updated", e); + } + } + + private static void getRenderAPI(APIInstanceProvider provider) { + try { + Object api = provider.getAPIInstance("render-1"); + MystRender.setAPI(api); + } catch (APIUndefined e) { + Objects.log.error("The Mystcraft render API is missing", e); + } catch (APIVersionUndefined e) { + Objects.log.error("Mystcraft can't count to 1, missing the render API", e); + } catch (APIVersionRemoved e) { + Objects.log.error("Version 1 of the Mystcraft render API was removed, NEI Mystcraft Plugin needs to be updated", e); + } + } + + private static void getPageAPI(APIInstanceProvider provider) { + try { + Object api = provider.getAPIInstance("page-1"); + MystPage.setAPI(api); + } catch (APIUndefined e) { + Objects.log.error("The Mystcraft page API is missing", e); + } catch (APIVersionUndefined e) { + Objects.log.error("Mystcraft can't count to 1, missing the page API", e); + } catch (APIVersionRemoved e) { + Objects.log.error("Version 1 of the Mystcraft page API was removed, NEI Mystcraft Plugin needs to be updated", e); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/subtypes/PageSubtypeInterpreter.java b/src/main/java/me/heldplayer/mystcraft_jei/subtypes/PageSubtypeInterpreter.java new file mode 100644 index 0000000..d4e5212 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/subtypes/PageSubtypeInterpreter.java @@ -0,0 +1,48 @@ +package me.heldplayer.mystcraft_jei.subtypes; + +import mezz.jei.api.ISubtypeRegistry.ISubtypeInterpreter; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; +import java.util.TreeSet; + +public class PageSubtypeInterpreter implements ISubtypeInterpreter { + public static final PageSubtypeInterpreter INSTANCE = new PageSubtypeInterpreter(); + + private PageSubtypeInterpreter() { + } + + @Nullable + @Override + public String getSubtypeInfo(@Nonnull ItemStack stack) { + NBTTagCompound tag = stack.getTagCompound(); + if (tag == null) { + return null; + } + boolean hasSymbol = tag.hasKey("symbol", 8); + boolean hasLinkPanel = tag.hasKey("linkpanel", 10); + if (hasSymbol && hasLinkPanel || !hasSymbol && !hasLinkPanel) { + return null; // Don't add info for pages that have more than one thing + } + + if (hasSymbol) { + return "Symbol:" + tag.getString("symbol"); + } else { + Set panelProperties = new TreeSet<>(); + + NBTTagCompound linkPanel = tag.getCompoundTag("linkpanel"); + if (linkPanel.hasKey("properties", 9)) { + NBTTagList properties = linkPanel.getTagList("properties", 8); + for (int i = 0; i < properties.tagCount(); i++) { + panelProperties.add(properties.getStringTagAt(i)); + } + } + + return "LinkPanel:" + String.join(";", panelProperties); + } + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/subtypes/UnlinkedLinkbookInterpreter.java b/src/main/java/me/heldplayer/mystcraft_jei/subtypes/UnlinkedLinkbookInterpreter.java new file mode 100644 index 0000000..15bbae0 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/subtypes/UnlinkedLinkbookInterpreter.java @@ -0,0 +1,40 @@ +package me.heldplayer.mystcraft_jei.subtypes; + +import mezz.jei.api.ISubtypeRegistry.ISubtypeInterpreter; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; +import java.util.TreeSet; + +public class UnlinkedLinkbookInterpreter implements ISubtypeInterpreter { + public static final UnlinkedLinkbookInterpreter INSTANCE = new UnlinkedLinkbookInterpreter(); + + private UnlinkedLinkbookInterpreter() { + } + + @Nullable + @Override + public String getSubtypeInfo(@Nonnull ItemStack stack) { + NBTTagCompound tag = stack.getTagCompound(); + + if (tag != null && tag.hasKey("linkpanel", 10)) { + Set panelProperties = new TreeSet<>(); + + NBTTagCompound linkPanel = tag.getCompoundTag("linkpanel"); + if (linkPanel.hasKey("properties", 9)) { + NBTTagList properties = linkPanel.getTagList("properties", 8); + for (int i = 0; i < properties.tagCount(); i++) { + panelProperties.add(properties.getStringTagAt(i)); + } + } + + return String.join(";", panelProperties); + } + + return ""; + } +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/util/Entry.java b/src/main/java/me/heldplayer/mystcraft_jei/util/Entry.java new file mode 100644 index 0000000..49819dd --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/util/Entry.java @@ -0,0 +1,11 @@ +package me.heldplayer.mystcraft_jei.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface Entry { +} diff --git a/src/main/java/me/heldplayer/mystcraft_jei/util/Integration.java b/src/main/java/me/heldplayer/mystcraft_jei/util/Integration.java new file mode 100644 index 0000000..61e6649 --- /dev/null +++ b/src/main/java/me/heldplayer/mystcraft_jei/util/Integration.java @@ -0,0 +1,188 @@ +package me.heldplayer.mystcraft_jei.util; + +import com.xcompwiz.mystcraft.api.MystObjects; +import com.xcompwiz.mystcraft.api.symbol.IAgeSymbol; +import com.xcompwiz.mystcraft.api.util.Color; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystItemFactory; +import me.heldplayer.mystcraft_jei.integration.mystcraft.MystSymbol; +import net.minecraft.crash.CrashReport; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.util.ReportedException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; + +public final class Integration { + + private static List allLinkbooks = Collections.emptyList(); + private static Field itemstack_bindings = null; + private static Field oredict_bindings = null; + private static Field itemId_bindings = null; + private static Field validInks = null; + private static Field inkcost = null; + + private Integration() { + } + + public static void initialize() { + Integration.prepareLinkbooks(); + Integration.getMethodsAndFields(); + } + + @Nullable + public static Item getMystItem(@Nonnull String name) { + Item item = Item.getByNameOrId(MystObjects.MystcraftModId + ":" + name); + if (item == null || item == Items.AIR) { + return null; + } + return item; + } + + @SuppressWarnings("unchecked") + private static void prepareLinkbooks() { + Map colormap; + + try { + Class inkEffectsClass = Class.forName("com.xcompwiz.mystcraft.data.InkEffects"); + Field colormapField = inkEffectsClass.getDeclaredField("colormap"); + colormapField.setAccessible(true); + // Add all modifiers known to have a colour, this includes mod added modifiers + colormap = (Map) colormapField.get(null); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + throw new ReportedException(CrashReport.makeCrashReport(e, "Failed initializing InkEffects")); + } + + if (!colormap.containsKey("Following")) { + colormap.put("Following", new Color(0, 0, 0)); // Add Following effect to display if not present + } + + Item unlinkedLinkbook = Integration.getMystItem(MystObjects.Items.linkbook_unlinked); + + if (unlinkedLinkbook != null) { + Integration.allLinkbooks = colormap.keySet() + .stream() + .filter(property -> !property.equals("Relative")) // Filter out Relative to match link panels + .sorted() + .map(property -> { + ItemStack is = new ItemStack(unlinkedLinkbook, 1, 0); + + NBTTagCompound compound = new NBTTagCompound(); + NBTTagCompound linkPanelCompound = new NBTTagCompound(); + + NBTTagList list = new NBTTagList(); + + list.appendTag(new NBTTagString(property)); + + linkPanelCompound.setTag("properties", list); + + compound.setTag("linkpanel", linkPanelCompound); + + // Tag structure: + /* + { + "linkpanel": { + "properties": [ property ] + } + } + */ + is.setTagCompound(compound); + + return is; + }).collect(Collectors.toList()); + } + } + + @Nonnull + public static List getAllLinkbooks() { + return Integration.allLinkbooks; + } + + private static void getMethodsAndFields() { + try { + Class inkEffectsClass = Class.forName("com.xcompwiz.mystcraft.data.InkEffects"); + + Integration.itemstack_bindings = inkEffectsClass.getDeclaredField("itemstack_bindings"); + Integration.itemstack_bindings.setAccessible(true); + + Integration.oredict_bindings = inkEffectsClass.getDeclaredField("oredict_bindings"); + Integration.oredict_bindings.setAccessible(true); + + Integration.itemId_bindings = inkEffectsClass.getDeclaredField("itemId_bindings"); + Integration.itemId_bindings.setAccessible(true); + + Class mystcraftClass = Class.forName("com.xcompwiz.mystcraft.Mystcraft"); + + Integration.validInks = mystcraftClass.getDeclaredField("validInks"); + Integration.validInks.setAccessible(true); + + Integration.inkcost = mystcraftClass.getDeclaredField("inkcost"); + Integration.inkcost.setAccessible(true); + } catch (ClassNotFoundException | NoSuchFieldException e) { + throw new ReportedException(CrashReport.makeCrashReport(e, "Failed initializing InkEffects")); + } + } + + @Nonnull + public static List getInkMixerInputs() { + ArrayList result = new ArrayList<>(); + + try { + Map itemstack_bindings = (Map) Integration.itemstack_bindings.get(null); + result.addAll(itemstack_bindings.keySet()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + try { + Map oredict_bindings = (Map) Integration.oredict_bindings.get(null); + result.addAll(oredict_bindings.keySet()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + try { + Map itemId_bindings = (Map) Integration.itemId_bindings.get(null); + result.addAll(itemId_bindings.keySet()); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + return result; + } + + @SuppressWarnings("unchecked") + public static Set getValidInks() { + try { + return (Set) Integration.validInks.get(null); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + return Collections.emptySet(); + } + + public static int getInkcost() { + try { + return Integration.inkcost.getInt(null); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + return 50; + } + + public static List gatherAllSymbolStacks() { + return MystSymbol.getAllRegisteredSymbols() + .stream() + .sorted(Comparator.comparing(IAgeSymbol::getLocalizedName)) + .map(IAgeSymbol::identifier) + .map(MystItemFactory::buildSymbolPage) + .collect(Collectors.toList()); + } +} diff --git a/src/main/resources/assets/mystcraft-jei-plugin/lang/en_US.lang b/src/main/resources/assets/mystcraft-jei-plugin/lang/en_US.lang new file mode 100644 index 0000000..00a370c --- /dev/null +++ b/src/main/resources/assets/mystcraft-jei-plugin/lang/en_US.lang @@ -0,0 +1,79 @@ +mystcraft-jei.writing_desk.page=Right click a page on the tab list on the right to craft +mystcraft-jei.writing_desk.page.name=Pages can't be renamed in the writing desk +mystcraft-jei.writing_desk.notebook=Right click a page on the tab list on the right to add it to the notebook +mystcraft-jei.writing_desk.notebook.name=Rename the notebook using the textfield + + +mystcraft-jei.ink_mixer.drop=Drop items in the ink well to add effects + +mystcraft-jei.description.decay.1=Decay is an instability in a Mystcraft Age that spreads. It causes destruction of the world and is usually hazardous to anything in the Age. +mystcraft-jei.description.decay.2=If you discover a certain kind of decay once in an Age, you can be sure you will find it again. +mystcraft-jei.description.decay.black.1=Black Decay eats the world, replacing blocks next to and below it. +mystcraft-jei.description.decay.black.2=Any blocks touching the decay on the top will slowly be consumed, causing the blocks above them to cascade down. +mystcraft-jei.description.decay.black.3=Black Decay will make itself visible by creating dips in the terrain, as if a crater is forming. +mystcraft-jei.description.decay.black.4=Eventually, a hole down to the void will be created. +mystcraft-jei.description.decay.red.1=Red Decay spreads throughout the world, converting any blocks in its path and even spreading into the air. +mystcraft-jei.description.decay.red.2=Red Decay spreads more slowly through blocks that are resistant to explosions. +mystcraft-jei.description.decay.blue.1=Blue Decay spreads throughout the world, converting any blocks in its path and even spreading into the air. +mystcraft-jei.description.decay.blue.2=Blue Decay spreads more slowly through blocks that are harder to break. +mystcraft-jei.description.decay.purple.1=Purple Decay spreads throughout the world, converting any blocks in its path and even spreading into the air. +mystcraft-jei.description.decay.purple.2=Purple Decay spreads more quickly through liquids +mystcraft-jei.description.decay.white.1=White Decay spreads throughout the world, converting any blocks in its path and even spreading into the air. +mystcraft-jei.description.decay.white.2=White Decay spreads very quickly, being only mildly hindered by air gaps. +mystcraft-jei.description.decay.white.3=Additionaly, it damages anything that touches it. +mystcraft-jei.description.decay.white.4=White Decay is the most dangerous kind of decay. + +mystcraft-jei.description.lectern.1=A Lectern lets you safely store a Descriptive Book or Linking Book to protect it from the elements. +mystcraft-jei.description.lectern.2=A book can be placed on a Lectern by using it on the Lectern. Taking it back out can be done by shift-using it with an empty hand. + +mystcraft-jei.description.bookstand.1=A Bookstand lets you safely store a Descriptive Book or Linking Book to protect it from the elements. +mystcraft-jei.description.bookstand.2=A book can be placed on a Bookstand by using it on the Bookstand. Taking it back out can be done by shift-using it with an empty hand. + +mystcraft-jei.description.crystal.1=Crystals are blocks that, when combined with a Book Receptacle, can form a portal to a different place or dimension. +mystcraft-jei.description.crystal.2=Crystals can be found in some Ages, either occouring randomly or because of writing in the Age's Descriptive Book. + +mystcraft-jei.description.crystal_receptacle.1=A Book Receptacle is a block that, when combined with Crystals, can form a portal to a different place or dimension. +mystcraft-jei.description.crystal_receptacle.2=A Descriptive Book or Linking Book can be placed in the receptacle to form a portal to the location the book links to. +mystcraft-jei.description.crystal_receptacle.3=Taking out the book will break the link and closes the portal. + +mystcraft-jei.description.ink_mixer.1=The Inker Mixer is used to create Link Panels. To create Link Panels, you need Ink Vials and Paper. +mystcraft-jei.description.ink_mixer.2=You can add effects to the Link Panel by putting items in the ink well. + +mystcraft-jei.description.writing_desk.1=The Writing Desk is used to create Symbol Pages. It is a useful block for planning out your Ages and for duplicating Symbol Pages. +mystcraft-jei.description.writing_desk.2=To create a new Symbol Page, you need to have an existing page or a Collation Folder or Symbol Portfolio that contains the Symbol. +mystcraft-jei.description.writing_desk.3=A Writing Desk can also be used to rename Descriptive Books and Linking Books. + +mystcraft-jei.description.book_binder.1=The Book Binder is used to create new Descriptive Books, letting you create a new Age. +mystcraft-jei.description.book_binder.2=While creating the Age, you can drop individual pages or a Collation Folder into the interface. +mystcraft-jei.description.book_binder.3=You must have exactly one Link Panel, and it must be the very first page. You also need leather and a name for the Age you are creating. +mystcraft-jei.description.book_binder.4=Adding Symbol Pages is optional, if you just want a random age, you can even go for only a Link Panel page. + +mystcraft-jei.description.link_modifer.1=The Link Modifier is a Debug block. It cannot be obtained legitimately. +mystcraft-jei.description.link_modifer.2=It can be used to change the linking properties of Linking Books and Description Books, as well as their names. +mystcraft-jei.description.link_modifer.3=If you want to change the name of a book, use a Writing Desk. If you want different Link Properties, create a new Linking Panel with the properties you want. + +mystcraft-jei.description.ink_vial.1=An Ink Vial is a Glass Bottle filled with Ink, the primary resource used to write Symbols and to create Link Panels. +mystcraft-jei.description.ink_vial.2=Ink Vials can be put into a Writing Desk or an Ink Mixer to begin writing Ages or creating Linking Books. + +mystcraft-jei.description.unlinked_linkbook.1=An Unlinked Link Book can be used to create a Linking Book, which will create a link to the location you are standing at. +mystcraft-jei.description.unlinked_linkbook.2=Linking can be done by using the Linking Book. + +mystcraft-jei.description.linked_linkbook.1=A Linking Book is a book that you can use to travel to a location. The location you travel to is where the book was made. +mystcraft-jei.description.linked_linkbook.2=Only Linking Books with the Intra-linking property can link to the same dimension. + +mystcraft-jei.description.sealed_notebook.1=Sealed Notebooks are collections of Symbol Pages that can be found in Mystcraft villager's homes and abandoned libraries in Ages. +mystcraft-jei.description.sealed_notebook.2=Using a Sealed Notebook will open it, and give you a Collation Folder with a bunch of randomly selected Symbols. + +mystcraft-jei.description.folder.1=Collation Folders let you store Pages in an ordered manner. +mystcraft-jei.description.folder.2=Dropping a Collation Folder in a Book Binder like a Page will put all the Pages in the Book Binder in the order they are stored. + +mystcraft-jei.description.portfolio.1=Symbol Portfolios let you store Pages in a sorted manner. +mystcraft-jei.description.portfolio.2=Pages stored in a Symbol Portfolio will always be sorted alphabetically. + +mystcraft-jei.category.book_binder=Book Binder +mystcraft-jei.category.ink_mixer=Ink Mixer +mystcraft-jei.category.writing_desk=Writing Desk + + + +linkeffect.following.name=Following diff --git a/src/main/resources/assets/mystcraft-jei-plugin/textures/gui/ages.png b/src/main/resources/assets/mystcraft-jei-plugin/textures/gui/ages.png new file mode 100644 index 0000000000000000000000000000000000000000..17d248382472b09ac8829c9eae89dcab64f851b2 GIT binary patch literal 1314 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$6&6|H(?D8gCb z5n0T@z;_UY8Fx&~ehU<2FY)wsWxvZU$S-TW?cuv#1_qY(o-U3d6}R5r@%IyP6gluP zysbb*;jrnh>2gg=@(-#Mvq$>HH>(vW-<`sp+thJp{;WpL*T2i$Do*wM-Mnk+bUl52 zWu-?SKN_-MQI<{dvitwJ{bTKO!SL{K>w3TIrG`9*!}cG}P1l=#Iyc>}{?E;tkI&!y zTE1wt{n9zbe(WE2O2@mGfr9}biM{^iS;n#BCyAJg^fvP^t0J1QCY z1}{0P*v!3=IqnOjsFLRPX;4U|1k4{hhHSR{XGm z$bYA2TP)j8F&p$He=M6(dAP^&?b(fu%Re!k$xq>&U3_b9GIM`AZu6j4y*cWpP~2w) zG$-M4ucc~n4$CSVhR@QGhn~$TdwzrCi6mAxeK5Rqy_k`Kg+YM90hF}<>6mpc7r3N zlHr;8rk1moZ_jNMJYfv62%`1D=cl{nu?5o=pk0J~<1lqQ!-3!1Pi(a_C}nDJGX0<3 z$AE~oU?RyiMZ!r8n!yr#bwR)*E{y^^}StlE}Gjb?%c`y zN{_P3xPei3;g>Ok#ns3EPcQ!KXj1lPKJ$biugY1=_Qyr6XiN`p-Ogb7d`pDR9;uMj zy>ae;swVip)O9##xpc~==`B%TYUkIvH#plc&sgT^yYijnf%Nk$t$-RlG+Wadw#c9oF$YB|G}o{6-H3hyKY=Jf*7X}`J3b#_JX=<~ zhRj{)uoGrsi-eZ|Na3Q5fQZ-|LR(Ugf9{m=D$t~qo4b^jFXBm7rNgwMGdVAs)V1{h zi9*w)f4ZD*CtQnY=Cc;=y~kLwC9$+4-fIZfoUP>yC7{h#QYM_hZUDVP;LA9^0JD6F UaWP|a{r~^~07*qoM6N<$g1gajSpWb4 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mystcraft-jei-plugin/textures/spacore/view-age.png b/src/main/resources/assets/mystcraft-jei-plugin/textures/spacore/view-age.png new file mode 100644 index 0000000000000000000000000000000000000000..ff48411f89d1d69697de3dd65fdb48b1cd08f285 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`S)MMAAr_~PPP)k3pupp@ehITh zko)e7R+FS3^t%ZiamapDd-2IV(Wr*~s%c@22kxe_?%DBMV8YDMmqO}S?^dWT`1-2n zWv-i5lV_6kYgvYqzeIvIWna12;BjpJhEMt?yE<}LK{@~c literal 0 HcmV?d00001 diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info new file mode 100644 index 0000000..d41ebcf --- /dev/null +++ b/src/main/resources/mcmod.info @@ -0,0 +1,13 @@ +[ + { + "modid": "mystcraft-jei-plugin", + "name": "Mystcraft JEI Plugin", + "description": "Adds JEI integration for Mystcraft", + "version": "${version}", + "mcversion": "${mcversion}", + "authorList": [ + "heldplayer" + ], + "url": "https://minecraft.curseforge.com/projects/jei-mystcraft-plugin" + } +]