From b2b13776f10f90cd07bef7ba9294a3d955ca3fcb Mon Sep 17 00:00:00 2001 From: jaredlll08 Date: Sat, 5 Sep 2015 16:51:34 +0200 Subject: [PATCH] 1.8 --- MineTweaker3-MC18-Main/.gitignore | 22 + MineTweaker3-MC18-Main/build.gradle | 82 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 52266 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + MineTweaker3-MC18-Main/gradlew | 164 ++++++ MineTweaker3-MC18-Main/gradlew.bat | 90 ++++ .../api/minecraft/MineTweakerMC.java | 496 ++++++++++++++++++ .../minetweaker/mc18/FMLEventHandler.java | 56 ++ .../minetweaker/mc18/ForgeEventHandler.java | 50 ++ .../minetweaker/mc18/GuiCannotRemodify.java | 60 +++ .../minetweaker/mc18/MCPlatformFunctions.java | 46 ++ .../java/minetweaker/mc18/MineTweakerMod.java | 183 +++++++ .../minetweaker/mc18/MineTweakerRegistry.java | 19 + .../mc18/actions/SetBlockHardnessAction.java | 84 +++ .../mc18/actions/SetStackSizeAction.java | 61 +++ .../mc18/actions/SetTranslationAction.java | 67 +++ .../mc18/block/MCBlockDefinition.java | 36 ++ .../minetweaker/mc18/block/MCItemBlock.java | 72 +++ .../mc18/block/MCSpecificBlock.java | 79 +++ .../minetweaker/mc18/block/MCWorldBlock.java | 96 ++++ .../mc18/brackets/ItemBracketHandler.java | 142 +++++ .../mc18/brackets/LiquidBracketHandler.java | 88 ++++ .../mc18/brackets/OreBracketHandler.java | 81 +++ .../minetweaker/mc18/chat/MCChatMessage.java | 37 ++ .../minetweaker/mc18/client/MCClient.java | 29 + .../minetweaker/mc18/data/NBTConverter.java | 172 ++++++ .../minetweaker/mc18/data/NBTUpdater.java | 48 ++ .../mc18/entity/MCEntityDefinition.java | 32 ++ .../formatting/FormattedMarkupString.java | 44 ++ .../mc18/formatting/FormattedString.java | 41 ++ .../mc18/formatting/FormattedStringJoin.java | 68 +++ .../mc18/formatting/IMCFormattedString.java | 19 + .../mc18/formatting/MCFormatter.java | 128 +++++ .../minetweaker/mc18/furnace/FuelTweaker.java | 95 ++++ .../mc18/furnace/MCFurnaceManager.java | 214 ++++++++ .../mc18/furnace/SetFuelPattern.java | 21 + .../java/minetweaker/mc18/game/MCGame.java | 214 ++++++++ .../mc18/item/MCItemDefinition.java | 90 ++++ .../minetweaker/mc18/item/MCItemStack.java | 391 ++++++++++++++ .../mc18/liquid/MCLiquidDefinition.java | 424 +++++++++++++++ .../mc18/liquid/MCLiquidStack.java | 210 ++++++++ .../minetweaker/mc18/mods/MCLoadedMods.java | 46 ++ .../java/minetweaker/mc18/mods/MCMod.java | 42 ++ .../MineTweakerCopyClipboardHandler.java | 33 ++ .../MineTweakerCopyClipboardPacket.java | 44 ++ .../MineTweakerLoadScriptsHandler.java | 33 ++ .../network/MineTweakerLoadScriptsPacket.java | 43 ++ .../MineTweakerOpenBrowserHandler.java | 38 ++ .../network/MineTweakerOpenBrowserPacket.java | 46 ++ .../minetweaker/mc18/oredict/MCOreDict.java | 39 ++ .../mc18/oredict/MCOreDictEntry.java | 395 ++++++++++++++ .../minetweaker/mc18/player/MCPlayer.java | 146 ++++++ .../mc18/recipes/MCCraftingInventory.java | 214 ++++++++ .../mc18/recipes/MCRecipeManager.java | 422 +++++++++++++++ .../mc18/recipes/RecipeConverter.java | 183 +++++++ .../mc18/recipes/ShapedRecipeAdvanced.java | 52 ++ .../mc18/recipes/ShapedRecipeBasic.java | 44 ++ .../mc18/recipes/ShapedRecipeOre.java | 38 ++ .../mc18/recipes/ShapelessRecipeAdvanced.java | 51 ++ .../mc18/recipes/ShapelessRecipeBasic.java | 41 ++ .../mc18/recipes/ShapelessRecipeOre.java | 38 ++ .../minetweaker/mc18/server/MCServer.java | 237 +++++++++ .../minetweaker/mc18/server/ServerPlayer.java | 127 +++++ .../mc18/util/MineTweakerHacks.java | 291 ++++++++++ .../mc18/util/MineTweakerObfuscation.java | 25 + .../mc18/util/MineTweakerPlatformUtils.java | 46 ++ .../mc18/vanilla/MCLootRegistry.java | 204 +++++++ .../mc18/vanilla/MCSeedRegistry.java | 150 ++++++ .../minetweaker/mc18/vanilla/MCVanilla.java | 30 ++ .../java/minetweaker/mc18/world/MCBiome.java | 27 + .../minetweaker/mc18/world/MCDimension.java | 45 ++ .../resources/assets/minetweaker/logo.png | Bin 0 -> 21583 bytes .../src/main/resources/mcmod.info | 16 + 73 files changed, 7543 insertions(+) create mode 100644 MineTweaker3-MC18-Main/.gitignore create mode 100644 MineTweaker3-MC18-Main/build.gradle create mode 100644 MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.jar create mode 100644 MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.properties create mode 100644 MineTweaker3-MC18-Main/gradlew create mode 100644 MineTweaker3-MC18-Main/gradlew.bat create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/api/minecraft/MineTweakerMC.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/FMLEventHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/ForgeEventHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/GuiCannotRemodify.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MCPlatformFunctions.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerMod.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerRegistry.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetBlockHardnessAction.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetStackSizeAction.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetTranslationAction.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCBlockDefinition.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCItemBlock.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCSpecificBlock.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCWorldBlock.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/ItemBracketHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/LiquidBracketHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/OreBracketHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/chat/MCChatMessage.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/client/MCClient.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTConverter.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTUpdater.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/entity/MCEntityDefinition.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedMarkupString.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedString.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedStringJoin.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/IMCFormattedString.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/MCFormatter.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/FuelTweaker.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/MCFurnaceManager.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/SetFuelPattern.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/game/MCGame.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemDefinition.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemStack.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidDefinition.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidStack.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCLoadedMods.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCMod.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardPacket.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsPacket.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserHandler.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserPacket.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDict.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDictEntry.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/player/MCPlayer.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCCraftingInventory.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCRecipeManager.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/RecipeConverter.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeAdvanced.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeBasic.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeOre.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeAdvanced.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeBasic.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeOre.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/MCServer.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/ServerPlayer.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerHacks.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerObfuscation.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerPlatformUtils.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCLootRegistry.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCSeedRegistry.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCVanilla.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCBiome.java create mode 100644 MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCDimension.java create mode 100644 MineTweaker3-MC18-Main/src/main/resources/assets/minetweaker/logo.png create mode 100644 MineTweaker3-MC18-Main/src/main/resources/mcmod.info diff --git a/MineTweaker3-MC18-Main/.gitignore b/MineTweaker3-MC18-Main/.gitignore new file mode 100644 index 0000000..2c770e0 --- /dev/null +++ b/MineTweaker3-MC18-Main/.gitignore @@ -0,0 +1,22 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run diff --git a/MineTweaker3-MC18-Main/build.gradle b/MineTweaker3-MC18-Main/build.gradle new file mode 100644 index 0000000..df1cb15 --- /dev/null +++ b/MineTweaker3-MC18-Main/build.gradle @@ -0,0 +1,82 @@ +/* +// For those who want the bleeding edge +buildscript { + repositories { + jcenter() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.0-SNAPSHOT' + } +} +apply plugin: 'net.minecraftforge.gradle.forge' +*/ + +// for people who want stable +plugins { + id "net.minecraftforge.gradle.forge" version "2.0.1" +} + +version = "1.0" +group= "com.yourname.modid" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "modid" + +minecraft { + version = "1.8-11.14.3.1506" + runDir = "run" + + // 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 allways work. + // simply re-run your setup task after changing the mappings to update your workspace. + mappings = "snapshot_20141130" + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. +} + +dependencies { + // 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' + + // replace version and mcversion + expand 'version':project.version, 'mcversion':project.minecraft.version + } + + // copy everything else, thats not the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} diff --git a/MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.jar b/MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..b5166dad4d90021f6a0b45268c0755719f1d5cd4 GIT binary patch literal 52266 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 z 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 literal 0 HcmV?d00001 diff --git a/MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.properties b/MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6be9a83 --- /dev/null +++ b/MineTweaker3-MC18-Main/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat May 16 19:56:41 CDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-bin.zip diff --git a/MineTweaker3-MC18-Main/gradlew b/MineTweaker3-MC18-Main/gradlew new file mode 100644 index 0000000..91a7e26 --- /dev/null +++ b/MineTweaker3-MC18-Main/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + 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/MineTweaker3-MC18-Main/gradlew.bat b/MineTweaker3-MC18-Main/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/MineTweaker3-MC18-Main/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/MineTweaker3-MC18-Main/src/main/java/minetweaker/api/minecraft/MineTweakerMC.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/api/minecraft/MineTweakerMC.java new file mode 100644 index 0000000..9501142 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/api/minecraft/MineTweakerMC.java @@ -0,0 +1,496 @@ +package minetweaker.api.minecraft; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.block.IBlock; +import minetweaker.api.block.IBlockDefinition; +import minetweaker.api.data.IData; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.IngredientUnknown; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.api.oredict.IOreDictEntry; +import minetweaker.api.player.IPlayer; +import minetweaker.api.world.IBiome; +import minetweaker.api.world.IDimension; +import minetweaker.mc18.block.MCBlockDefinition; +import minetweaker.mc18.block.MCSpecificBlock; +import minetweaker.mc18.block.MCWorldBlock; +import minetweaker.mc18.data.NBTConverter; +import minetweaker.mc18.item.MCItemStack; +import minetweaker.mc18.liquid.MCLiquidStack; +import minetweaker.mc18.oredict.MCOreDictEntry; +import minetweaker.mc18.player.MCPlayer; +import minetweaker.mc18.world.MCBiome; +import minetweaker.mc18.world.MCDimension; +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * MineTweaker - MineCraft API bridge. + * + * @author Stan Hebben + */ +public class MineTweakerMC { + private static final Map blockDefinitions = new HashMap(); + public static final IBiome[] biomes; + + static { + biomes = new IBiome[BiomeGenBase.getBiomeGenArray().length]; + for (int i = 0; i < BiomeGenBase.getBiomeGenArray().length; i++) { + if (BiomeGenBase.getBiomeGenArray()[i] != null) + biomes[i] = new MCBiome(BiomeGenBase.getBiomeGenArray()[i]); + } + } + + private MineTweakerMC() { + } + + /** + * Returns the Minecraft item for this MineTweaker item. + * + * @param item minetweaker item stack + * @return minecraft item stack + */ + public static ItemStack getItemStack(IItemStack item) { + if (item == null) + return null; + + Object internal = item.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + MineTweakerAPI.logError("Not a valid item stack: " + item); + } + return (ItemStack) internal; + } + + /** + * Returns the Minecraft ingredient for this ingredient. This method is only + * useful for ingredients that represent a single item stack. + * + * @param ingredient item ingredient + * @return minecraft item stack + */ + public static ItemStack getItemStack(IIngredient ingredient) { + if (ingredient == null) + return null; + + List items = ingredient.getItems(); + if (items.size() != 1) { + MineTweakerAPI.logError("Not an ingredient with a single item: " + ingredient); + } + return getItemStack(items.get(0)); + } + + /** + * Returns the MineTweaker item stack for this item. + * + * @param item minecraft item stack + * @return minetweaker item stack + */ + public static IItemStack getIItemStack(ItemStack item) { + if (item == null) + return null; + + return new MCItemStack(item); + } + + /** + * Constructs an item stack with wildcard size. + * + * @param item minecraft item stack + * @return minetweaker stack + */ + public static IItemStack getIItemStackWildcardSize(ItemStack item) { + if (item == null) + return null; + + return new MCItemStack(item, true); + } + + /** + * Constructs an item stack with wildcard size. + * + * @param item + * @param meta + * @return minetweaker stack + */ + public static IItemStack getIItemStackWildcardSize(Item item, int meta) { + if (item == null) + return null; + + return new MCItemStack(new ItemStack(item, 1, meta), true); + } + + /** + * Constructs an item stack with wildcard damage. + * + * @param item stack item + * @param amount stack size + * @return minetweaker item stack + */ + public static IItemStack getIItemStackWildcard(Item item, int amount) { + if (item == null) + return null; + + return new MCItemStack(new ItemStack(item, 1, OreDictionary.WILDCARD_VALUE)); + } + + public static ItemStack[] getExamples(IIngredient ingredient) { + List examples = ingredient.getItems(); + ItemStack[] result = new ItemStack[examples.size()]; + for (int i = 0; i < examples.size(); i++) { + result[i] = MineTweakerMC.getItemStack(examples.get(i)); + } + return result; + } + + /** + * Constructs an item stack with given item, damage and amount. + * + * @param item stack item + * @param damage stack damage + * @param amount stack amount + * @return minetweaker item stack + */ + public static IItemStack getItemStack(Item item, int amount, int damage) { + if (item == null) + return null; + + return new MCItemStack(new ItemStack(item, amount, damage)); + } + + /** + * Converts an array of minetweaker item stacks into an array of minecraft + * item stacks. + * + * @param items minetweker item stacks + * @return minecraft item stacks + */ + public static ItemStack[] getItemStacks(IItemStack... items) { + if (items == null) + return null; + + ItemStack[] output = new ItemStack[items.length]; + for (int i = 0; i < items.length; i++) { + Object internal = items[i].getInternal(); + if (internal != null && internal instanceof ItemStack) { + output[i] = (ItemStack) internal; + } + } + return output; + } + + /** + * Converts a list of minetweaker item stacks into an array of minecraft + * item stacks. + * + * @param items minetweaker items + * @return minecraft items + */ + public static ItemStack[] getItemStacks(List items) { + if (items == null) + return null; + + ItemStack[] output = new ItemStack[items.size()]; + for (int i = 0; i < items.size(); i++) { + Object internal = items.get(i).getInternal(); + if (internal != null && internal instanceof ItemStack) { + output[i] = (ItemStack) internal; + } else { + MineTweakerAPI.logError("Invalid item stack: " + items.get(i)); + } + } + return output; + } + + /** + * Converts an array of minecraft item stacks into an array of minetweaker + * item stacks. + * + * @param items minecraft item stacks + * @return minetweaker item stacks + */ + public static IItemStack[] getIItemStacks(ItemStack... items) { + if (items == null) + return null; + + IItemStack[] result = new IItemStack[items.length]; + for (int i = 0; i < items.length; i++) { + result[i] = new MCItemStack(items[i]); + } + return result; + } + + /** + * Converts a list of minecraft item stacks into an array of minetweaker + * item stacks. + * + * @param items minecraft item stacks + * @return minetweaker item stacks + */ + public static IItemStack[] getIItemStacks(List items) { + if (items == null) + return null; + + IItemStack[] result = new IItemStack[items.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = new MCItemStack(items.get(i)); + } + return result; + } + + /** + * Gets the ore dictionary entry with the given name. If the entry didn't + * exist yet, it will create it and return an empty entry. + * + * @param name ore entry name + * @return ore dictionary entry + */ + public static IOreDictEntry getOreDict(String name) { + return new MCOreDictEntry(name); + } + + /** + * Converts a Minecraft player into a MineTweaker player. + * + * @param player minecraft player + * @return minetweaker player + */ + public static IPlayer getIPlayer(EntityPlayer player) { + if (player == null) + return null; + + return new MCPlayer(player); + } + + /** + * Converts a MineTweaker player into a Minecraft player. + * + * @param player minetweaker player + * @return minecraft player + */ + public static EntityPlayer getPlayer(IPlayer player) { + if (player == null) + return null; + + if (!(player instanceof MCPlayer)) { + MineTweakerAPI.logError("Invalid player: " + player); + } + + return ((MCPlayer) player).getInternal(); + } + + /** + * Converts a Minecraft NBT to an IData instance. The IData instance is + * immutable (not modifyiable). + * + * @param nbt nbt data + * @return IData value + */ + public static IData getIData(NBTBase nbt) { + if (nbt == null) + return null; + + return NBTConverter.from(nbt, true); + } + + /** + * Converts a Minecraft NBT to a modifyable IData instance. + * + * @param nbt nbt data + * @return mutable IData value + */ + public static IData getIDataModifyable(NBTBase nbt) { + if (nbt == null) + return null; + + return NBTConverter.from(nbt, false); + } + + /** + * Converts an IData instance to an NBT value. + * + * @param data IData value + * @return nbt data + */ + public static NBTBase getNBT(IData data) { + if (data == null) + return null; + + return NBTConverter.from(data); + } + + /** + * Converts an IData instance to an NBT Tag compound. + * + * @param data IData value + * @return nbt compound data + */ + public static NBTTagCompound getNBTCompound(IData data) { + if (data == null) + return null; + + return (NBTTagCompound) NBTConverter.from(data); + } + + /** + * Retrieves the block at the given position. + * + * @param blocks block access + * @param x block x position + * @param y block y position + * @param z block z position + * @return block instance + */ + public static IBlock getBlock(IBlockAccess blocks, int x, int y, int z) { + return new MCWorldBlock(blocks, x, y, z); + } + + /** + * Retrieves the block definition for the given block. + * + * @param block block object + * @return block definition + */ + public static IBlockDefinition getBlockDefinition(Block block) { + if (!blockDefinitions.containsKey(block)) { + blockDefinitions.put(block, new MCBlockDefinition(block)); + } + return blockDefinitions.get(block); + } + + /** + * Retrieves the dimension instance for a given world. + * + * @param world world + * @return dimension + */ + public static IDimension getDimension(World world) { + if (world == null) + return null; + + return new MCDimension(world); + } + + /** + * Returns an instance of the given block. + * + * @param block MC block definition + * @return MT block instance + */ + public static IBlock getBlockAnyMeta(Block block) { + return new MCSpecificBlock(block, OreDictionary.WILDCARD_VALUE); + } + + /** + * Returns an instance of the given block. + * + * @param block MC block definition + * @param meta block meta value + * @return MT block instance + */ + public static IBlock getBlock(Block block, int meta) { + return new MCSpecificBlock(block, meta); + } + + /** + * Retrieves the block from an item stack. + * + * @param itemStack + * @return + */ + public static Block getBlock(IItemStack itemStack) { + return ((MCBlockDefinition) itemStack.asBlock().getDefinition()).getInternalBlock(); + } + + public static Block getBlock(IBlock block) { + return ((MCBlockDefinition) block.getDefinition()).getInternalBlock(); + } + + /** + * Retrieves the internal fluid stack of the given stack. + * + * @param stack MT liquid stack + * @return MCF fluid stack + */ + public static FluidStack getLiquidStack(ILiquidStack stack) { + if (stack == null) + return null; + + return (FluidStack) stack.getInternal(); + } + + /** + * Converts an array of MT liquid stacks into an array of MCF fluid stacks + * + */ + public static FluidStack[] getLiquidStacks(ILiquidStack[] stacks) { + if (stacks == null) { + return null; + } + + FluidStack[] res = new FluidStack[stacks.length]; + + for (int i = 0; i < stacks.length; i++) { + ILiquidStack liquidStack = stacks[i]; + res[i] = getLiquidStack(liquidStack); + } + return res; + } + + private static final HashMap oreDictArrays = new HashMap(); + + public static IOreDictEntry getOreDictEntryFromArray(List array) { + if (!oreDictArrays.containsKey(array)) { + for (String ore : OreDictionary.getOreNames()) { + if (OreDictionary.getOres(ore) == array) { + oreDictArrays.put(array, MineTweakerAPI.oreDict.get(ore)); + } + } + } + + return oreDictArrays.get(array); + } + + /** + * Converts a Minecraft ingredient to a MineTweaker ingredient. + * + * @param ingredient minecraft ingredient + * @return minetweaker ingredient + */ + public static IIngredient getIIngredient(Object ingredient) { + if (ingredient == null) { + return null; + } else if (ingredient instanceof String) { + return MineTweakerAPI.oreDict.get((String) ingredient); + } else if (ingredient instanceof Item) { + return getIItemStack(new ItemStack((Item) ingredient, 1, 0)); + } else if (ingredient instanceof ItemStack) { + return getIItemStack((ItemStack) ingredient); + } else if (ingredient instanceof List) { + IOreDictEntry entry = getOreDictEntryFromArray((List) ingredient); + + if (entry == null) { + return IngredientUnknown.INSTANCE; + } + + return entry; + } else if (ingredient instanceof FluidStack) { + return new MCLiquidStack((FluidStack) ingredient); + } else { + throw new IllegalArgumentException("Not a valid ingredient: " + ingredient); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/FMLEventHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/FMLEventHandler.java new file mode 100644 index 0000000..f544987 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/FMLEventHandler.java @@ -0,0 +1,56 @@ +package minetweaker.mc18; + +import minetweaker.MineTweakerAPI; +import minetweaker.MineTweakerImplementationAPI; +import minetweaker.api.event.PlayerCraftedEvent; +import minetweaker.api.event.PlayerLoggedInEvent; +import minetweaker.api.event.PlayerLoggedOutEvent; +import minetweaker.api.event.PlayerSmeltedEvent; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.player.IPlayer; +import minetweaker.mc18.network.MineTweakerLoadScriptsPacket; +import minetweaker.mc18.recipes.MCCraftingInventory; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * + * @author Stan + */ +public class FMLEventHandler { + @SubscribeEvent + public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent ev) { + if (ev.player instanceof EntityPlayerMP) { + EntityPlayerMP player = (EntityPlayerMP) ev.player; + MineTweakerMod.NETWORK.sendTo(new MineTweakerLoadScriptsPacket(MineTweakerAPI.tweaker.getScriptData()), player); + } + + MineTweakerImplementationAPI.events.publishPlayerLoggedIn(new PlayerLoggedInEvent(MineTweakerMC.getIPlayer(ev.player))); + } + + @SubscribeEvent + public void onPlayerItemCrafted(PlayerEvent.ItemCraftedEvent ev) { + IPlayer iPlayer = MineTweakerMC.getIPlayer(ev.player); + if (MineTweakerMod.INSTANCE.recipes.hasTransformerRecipes()) { + MineTweakerMod.INSTANCE.recipes.applyTransformations(MCCraftingInventory.get(ev.craftMatrix, ev.player), iPlayer); + } + + if (MineTweakerImplementationAPI.events.hasPlayerCrafted()) { + MineTweakerImplementationAPI.events.publishPlayerCrafted(new PlayerCraftedEvent(iPlayer, MineTweakerMC.getIItemStack(ev.crafting), MCCraftingInventory.get(ev.craftMatrix, ev.player))); + } + } + + @SubscribeEvent + public void onPlayerItemSmelted(PlayerEvent.ItemSmeltedEvent ev) { + if (MineTweakerImplementationAPI.events.hasPlayerSmelted()) { + MineTweakerImplementationAPI.events.publishPlayerSmelted(new PlayerSmeltedEvent(MineTweakerMC.getIPlayer(ev.player), MineTweakerMC.getIItemStack(ev.smelting))); + } + } + + @SubscribeEvent + public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent ev) { + MineTweakerImplementationAPI.events.publishPlayerLoggedOut(new PlayerLoggedOutEvent(MineTweakerMC.getIPlayer(ev.player))); + } + +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/ForgeEventHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/ForgeEventHandler.java new file mode 100644 index 0000000..11874be --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/ForgeEventHandler.java @@ -0,0 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18; + +import minetweaker.MineTweakerImplementationAPI; +import minetweaker.api.formatting.IFormattedText; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.tooltip.IngredientTooltips; +import minetweaker.mc18.formatting.IMCFormattedString; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import org.lwjgl.input.Keyboard; + +/** + * + * @author Stan + */ +public class ForgeEventHandler { + @SubscribeEvent + public void onPlayerInteract(PlayerInteractEvent ev) { + minetweaker.api.event.PlayerInteractEvent event = new minetweaker.api.event.PlayerInteractEvent( + MineTweakerMC.getIPlayer(ev.entityPlayer), + MineTweakerMC.getDimension(ev.world), + ev.pos.getX(), ev.pos.getY(), ev.pos.getZ() + ); + + MineTweakerImplementationAPI.events.publishPlayerInteract(event); + } + + @SubscribeEvent + public void onItemTooltip(ItemTooltipEvent ev) { + IItemStack itemStack = MineTweakerMC.getIItemStack(ev.itemStack); + for (IFormattedText tooltip : IngredientTooltips.getTooltips(itemStack)) { + ev.toolTip.add(((IMCFormattedString) tooltip).getTooltipString()); + } + + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + for (IFormattedText tooltip : IngredientTooltips.getShiftTooltips(itemStack)) { + ev.toolTip.add(((IMCFormattedString) tooltip).getTooltipString()); + } + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/GuiCannotRemodify.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/GuiCannotRemodify.java new file mode 100644 index 0000000..5b84a46 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/GuiCannotRemodify.java @@ -0,0 +1,60 @@ +package minetweaker.mc18; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; + +/** + * This screen is shown when a client has been modified with a permanent + * modification and the player logs into a game with a different script. It asks + * the player to restart the game. + * + * @author Stan Hebben + */ +public class GuiCannotRemodify extends GuiScreen { + private final String message1; + private final String message2; + private final String message3; + + public GuiCannotRemodify(String message1, String message2, String message3) { + this.message1 = message1; + this.message2 = message2; + this.message3 = message3; + } + + @Override + public void initGui() { + this.buttonList.clear(); + this.buttonList.add( + new GuiButton(0, this.width / 2 - 100, this.height / 4 + 120 + 12, "Close") + ); + } + + @Override + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString( + this.fontRendererObj, + message1, + this.width / 2, this.height / 2 - 40, + 11184810); + this.drawCenteredString( + this.fontRendererObj, + message2, + this.width / 2, this.height / 2 - 20, + 11184810); + this.drawCenteredString( + this.fontRendererObj, + message3, + this.width / 2, this.height / 2 - 0, + 11184810); + + super.drawScreen(par1, par2, par3); + } + + @Override + protected void actionPerformed(GuiButton par1GuiButton) { + if (par1GuiButton.id == 0) { + System.exit(0); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MCPlatformFunctions.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MCPlatformFunctions.java new file mode 100644 index 0000000..ff857c4 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MCPlatformFunctions.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18; + +import static minetweaker.mc18.MineTweakerMod.NETWORK; +import minetweaker.IPlatformFunctions; +import minetweaker.api.chat.IChatMessage; +import minetweaker.api.item.IItemDefinition; +import minetweaker.mc18.chat.MCChatMessage; +import minetweaker.mc18.item.MCItemDefinition; +import minetweaker.mc18.network.MineTweakerLoadScriptsPacket; +import net.minecraft.item.Item; + +/** + * + * @author Stan + */ +public class MCPlatformFunctions implements IPlatformFunctions { + public static final MCPlatformFunctions INSTANCE = new MCPlatformFunctions(); + + private MCPlatformFunctions() { + } + + @Override + public IChatMessage getMessage(String message) { + return new MCChatMessage(message); + } + + @Override + public void distributeScripts(byte[] data) { + NETWORK.sendToAll(new MineTweakerLoadScriptsPacket(data)); + } + + @Override + public IItemDefinition getItemDefinition(int id) { + Item item = Item.getItemById(id); + if (item == null) + return null; + String sid = (String) Item.itemRegistry.getNameForObject(item); + return new MCItemDefinition(sid, item); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerMod.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerMod.java new file mode 100644 index 0000000..f2e20e3 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerMod.java @@ -0,0 +1,183 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18; + +import java.io.File; + +import minetweaker.MineTweakerAPI; +import minetweaker.MineTweakerImplementationAPI; +import minetweaker.api.logger.FileLogger; +import minetweaker.mc18.brackets.ItemBracketHandler; +import minetweaker.mc18.client.MCClient; +import minetweaker.mc18.formatting.MCFormatter; +import minetweaker.mc18.furnace.FuelTweaker; +import minetweaker.mc18.furnace.MCFurnaceManager; +import minetweaker.mc18.game.MCGame; +import minetweaker.mc18.mods.MCLoadedMods; +import minetweaker.mc18.network.MineTweakerCopyClipboardHandler; +import minetweaker.mc18.network.MineTweakerCopyClipboardPacket; +import minetweaker.mc18.network.MineTweakerLoadScriptsHandler; +import minetweaker.mc18.network.MineTweakerLoadScriptsPacket; +import minetweaker.mc18.network.MineTweakerOpenBrowserHandler; +import minetweaker.mc18.network.MineTweakerOpenBrowserPacket; +import minetweaker.mc18.oredict.MCOreDict; +import minetweaker.mc18.recipes.MCRecipeManager; +import minetweaker.mc18.server.MCServer; +import minetweaker.mc18.util.MineTweakerHacks; +import minetweaker.mc18.util.MineTweakerPlatformUtils; +import minetweaker.mc18.vanilla.MCVanilla; +import minetweaker.runtime.IScriptProvider; +import minetweaker.runtime.providers.ScriptProviderCascade; +import minetweaker.runtime.providers.ScriptProviderCustom; +import minetweaker.runtime.providers.ScriptProviderDirectory; +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.event.FMLInterModComms; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerAboutToStartEvent; +import net.minecraftforge.fml.common.event.FMLServerStartingEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppedEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; +import net.minecraftforge.fml.relauncher.Side; + +/** + * Main mod class. Performs some general logic, initialization of the API and + * FML event handling. + * + * @author Stan Hebben + */ +@Mod(modid = MineTweakerMod.MODID, version = "3.0.9B") +public class MineTweakerMod { + public static final String MODID = "MineTweaker3"; + public static final String MCVERSION = "1.8"; + + public static final SimpleNetworkWrapper NETWORK = NetworkRegistry.INSTANCE.newSimpleChannel(MODID); + + private static final String[] REGISTRIES = { + "minetweaker.mods.ic2.ClassRegistry", + "minetweaker.mods.nei.ClassRegistry", + "minetweaker.mods.mfr.ClassRegistry", + "minetweaker.mods.gregtech.ClassRegistry", + "minetweaker.mods.buildcraft.ClassRegistry" + }; + private static final String[] REGISTRY_DESCRIPTIONS = { + "IC2 mod support", + "NEI mod support", + "MFR mod support", + "GregTech mod support", + "Buildcraft mod support" + }; + + static { + NETWORK.registerMessage(MineTweakerLoadScriptsHandler.class, MineTweakerLoadScriptsPacket.class, 0, Side.CLIENT); + NETWORK.registerMessage(MineTweakerOpenBrowserHandler.class, MineTweakerOpenBrowserPacket.class, 1, Side.CLIENT); + NETWORK.registerMessage(MineTweakerCopyClipboardHandler.class, MineTweakerCopyClipboardPacket.class, 2, Side.CLIENT); + } + + @Mod.Instance(MODID) + public static MineTweakerMod INSTANCE; + + public final MCRecipeManager recipes; + private final IScriptProvider scriptsGlobal; + private final ScriptProviderCustom scriptsIMC; + + public MineTweakerMod() { + MineTweakerImplementationAPI.init( + new MCOreDict(), + recipes = new MCRecipeManager(), + new MCFurnaceManager(), + MCGame.INSTANCE, + new MCLoadedMods(), + new MCFormatter(), + new MCVanilla()); + + MineTweakerImplementationAPI.logger.addLogger(new FileLogger(new File("minetweaker.log"))); + MineTweakerImplementationAPI.platform = MCPlatformFunctions.INSTANCE; + + File globalDir = new File("scripts"); + if (!globalDir.exists()) + globalDir.mkdirs(); + + scriptsIMC = new ScriptProviderCustom("intermod"); + scriptsGlobal = new ScriptProviderDirectory(globalDir); + MineTweakerImplementationAPI.setScriptProvider(scriptsGlobal); + } + + // ########################## + // ### FML Event Handlers ### + // ########################## + + @EventHandler + public void onIMCEvent(FMLInterModComms.IMCEvent event) { + for (final FMLInterModComms.IMCMessage imcMessage : event.getMessages()) { + if (imcMessage.key.equalsIgnoreCase("addMineTweakerScript")) { + if (imcMessage.isStringMessage()) { + scriptsIMC.add(imcMessage.getSender() + "::imc", imcMessage.getStringValue()); + } else if (imcMessage.isNBTMessage()) { + NBTTagCompound message = imcMessage.getNBTValue(); + scriptsIMC.add(imcMessage.getSender() + "::" + message.getString("name"), message.getString("content")); + } + } + } + } + + @EventHandler + public void onLoad(FMLPreInitializationEvent ev) { + MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); + FMLCommonHandler.instance().bus().register(new FMLEventHandler()); + } + + @EventHandler + public void onPostInit(FMLPostInitializationEvent ev) { + MineTweakerAPI.registerClassRegistry(MineTweakerRegistry.class); + + for (int i = 0; i < REGISTRIES.length; i++) { + MineTweakerAPI.registerClassRegistry(REGISTRIES[i], REGISTRY_DESCRIPTIONS[i]); + } + + FuelTweaker.INSTANCE.register(); + + } + + @EventHandler + public void onServerAboutToStart(FMLServerAboutToStartEvent ev) { + // starts before loading worlds + // perfect place to start MineTweaker! + + if (MineTweakerPlatformUtils.isClient()) { + MineTweakerAPI.client = new MCClient(); + } + + File scriptsDir = new File(MineTweakerHacks.getWorldDirectory(ev.getServer()), "scripts"); + if (!scriptsDir.exists()) { + scriptsDir.mkdir(); + } + + IScriptProvider scriptsLocal = new ScriptProviderDirectory(scriptsDir); + IScriptProvider cascaded = new ScriptProviderCascade(scriptsIMC, scriptsGlobal, scriptsLocal); + + MineTweakerImplementationAPI.setScriptProvider(cascaded); + MineTweakerImplementationAPI.onServerStart(new MCServer(ev.getServer())); + } + + @EventHandler + public void onServerStarting(FMLServerStartingEvent ev) { + + } + + @EventHandler + public void onServerStopped(FMLServerStoppedEvent ev) { + MineTweakerImplementationAPI.onServerStop(); + MineTweakerImplementationAPI.setScriptProvider(scriptsGlobal); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerRegistry.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerRegistry.java new file mode 100644 index 0000000..87c8181 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/MineTweakerRegistry.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18; + +import java.util.List; + +/** + * + * @author Stan + */ +public class MineTweakerRegistry { + public static void getClasses(List classes) { + // method stub, actual content is generated by build process + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetBlockHardnessAction.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetBlockHardnessAction.java new file mode 100644 index 0000000..d84f5ce --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetBlockHardnessAction.java @@ -0,0 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.actions; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.ReflectionHelper; + +/** + * + * @author Jared + */ +public class SetBlockHardnessAction implements IUndoableAction { + + private final ItemStack stack; + private final float hardness; + private final float oldHardness; + + public SetBlockHardnessAction(ItemStack stack, float hardness) { + this.stack = stack; + this.hardness = hardness; + this.oldHardness = ReflectionHelper.getPrivateValue(Block.class, Block.getBlockFromItem(stack.getItem()), "blockHardness"); + } + + @Override + public void apply() { + if (isBlock(stack)) { + set(stack, hardness); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + if (isBlock(stack)) + set(stack, oldHardness); + } + + @Override + public String describe() { + if (isBlock(stack)) { + return "Setting hardness of " + stack.getDisplayName() + " to " + hardness; + } + return "Unable to set hardness of " + stack.getDisplayName() + " because it is an Item"; + } + + @Override + public String describeUndo() { + if (isBlock(stack)) { + return "Reverting hardness of " + stack.getDisplayName() + " to " + oldHardness; + } + return "Unable to revert hardness of " + stack.getDisplayName() + " because it is an Item"; + } + + private static void set(ItemStack stack, float hardness) { + if (isBlock(stack)) { + Block block = Block.getBlockFromItem(stack.getItem()); + block.setHardness(hardness); + } else { + MineTweakerAPI.logError("Item is not a block"); + } + } + + public static boolean isBlock(ItemStack stack) { + String name = (String) Block.blockRegistry.getNameForObject(Block.getBlockFromItem(stack.getItem())); + return !name.equalsIgnoreCase("minecraft:air") && Block.blockRegistry.containsKey(name); + + } + + @Override + public Object getOverrideKey() { + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetStackSizeAction.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetStackSizeAction.java new file mode 100644 index 0000000..300e25a --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetStackSizeAction.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.actions; + +import minetweaker.IUndoableAction; +import net.minecraft.item.ItemStack; + +/** + * + * @author Jared + */ +public class SetStackSizeAction implements IUndoableAction { + + private final ItemStack stack; + private final int size; + private final int oldSize; + + public SetStackSizeAction(ItemStack stack, int size) { + this.stack = stack; + this.size = size; + this.oldSize = stack.getMaxStackSize(); + } + + @Override + public void apply() { + set(stack, size); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + set(stack, oldSize); + } + + @Override + public String describe() { + return "Setting max stack size of " + stack.getDisplayName() + " to " + size; + } + + @Override + public String describeUndo() { + return "Reverting max stack size of " + stack.getDisplayName() + " to " + oldSize; + } + + private static void set(ItemStack stack, int size) { + stack.getItem().setMaxStackSize(size); + } + + @Override + public Object getOverrideKey() { + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetTranslationAction.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetTranslationAction.java new file mode 100644 index 0000000..c8882f3 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/actions/SetTranslationAction.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.actions; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; + +import minetweaker.IUndoableAction; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.util.StringTranslate; + +/** + * + * @author Stan + */ +public class SetTranslationAction implements IUndoableAction { + private static final StringTranslate INSTANCE = MineTweakerHacks.getStringTranslateInstance(); + private static final Charset UTF8 = Charset.forName("utf-8"); + + private final String key; + private final String newValue; + private final String oldValue; + + public SetTranslationAction(String key, String value) { + this.key = key; + newValue = value; + oldValue = INSTANCE.translateKey(key); + } + + @Override + public void apply() { + set(key, newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + set(key, oldValue); + } + + @Override + public String describe() { + return "Translating " + key + " to " + newValue; + } + + @Override + public String describeUndo() { + return "Reverting " + key + " to " + oldValue; + } + + private static void set(String key, String value) { + StringTranslate.inject(new ByteArrayInputStream((key + "=" + value).getBytes(UTF8))); + } + + @Override + public Object getOverrideKey() { + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCBlockDefinition.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCBlockDefinition.java new file mode 100644 index 0000000..43d5ff7 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCBlockDefinition.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.block; + +import minetweaker.api.block.IBlockDefinition; +import net.minecraft.block.Block; + +/** + * + * @author Stan + */ +public class MCBlockDefinition implements IBlockDefinition { + private final Block block; + + public MCBlockDefinition(Block block) { + this.block = block; + } + + public Block getInternalBlock() { + return block; + } + + @Override + public String getId() { + return (String) Block.blockRegistry.getNameForObject(block); + } + + @Override + public String getDisplayName() { + return block.getLocalizedName(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCItemBlock.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCItemBlock.java new file mode 100644 index 0000000..fa2f109 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCItemBlock.java @@ -0,0 +1,72 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.block; + +import java.util.Collections; +import java.util.List; + +import minetweaker.api.block.BlockPatternOr; +import minetweaker.api.block.IBlock; +import minetweaker.api.block.IBlockDefinition; +import minetweaker.api.block.IBlockPattern; +import minetweaker.api.data.IData; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCItemBlock implements IBlock { + private final ItemStack item; + + public MCItemBlock(ItemStack item) { + this.item = item; + } + + @Override + public IBlockDefinition getDefinition() { + return MineTweakerMC.getBlockDefinition(Block.getBlockFromItem(item.getItem())); + } + + @Override + public int getMeta() { + return item.getItemDamage(); + } + + @Override + public IData getTileData() { + if (item.getTagCompound()== null) + return null; + + return MineTweakerMC.getIData(item.getTagCompound()); + } + + @Override + public String getDisplayName() { + return item.getDisplayName(); + } + + @Override + public List getBlocks() { + return Collections.singletonList(this); + } + + @Override + public boolean matches(IBlock block) { + return getDefinition() == block.getDefinition() + && (getMeta() == OreDictionary.WILDCARD_VALUE || getMeta() == block.getMeta()) + && (getTileData() == null || (block.getTileData() != null && block.getTileData().contains(getTileData()))); + } + + @Override + public IBlockPattern or(IBlockPattern pattern) { + return new BlockPatternOr(this, pattern); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCSpecificBlock.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCSpecificBlock.java new file mode 100644 index 0000000..06a3be8 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCSpecificBlock.java @@ -0,0 +1,79 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.block; + +import java.util.Collections; +import java.util.List; + +import minetweaker.api.block.BlockPatternOr; +import minetweaker.api.block.IBlock; +import minetweaker.api.block.IBlockDefinition; +import minetweaker.api.block.IBlockPattern; +import minetweaker.api.data.IData; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraft.block.Block; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCSpecificBlock implements IBlock { + private final Block block; + private final int meta; + + public MCSpecificBlock(Block block, int meta) { + this.block = block; + this.meta = meta; + } + + @Override + public IBlockDefinition getDefinition() { + return MineTweakerMC.getBlockDefinition(block); + } + + @Override + public int getMeta() { + return meta; + } + + @Override + public IData getTileData() { + return null; + } + + @Override + public List getBlocks() { + return Collections.singletonList(this); + } + + @Override + public boolean matches(IBlock block) { + return block.getDefinition() == getDefinition() + && (meta == OreDictionary.WILDCARD_VALUE || block.getMeta() == meta); + } + + @Override + public IBlockPattern or(IBlockPattern pattern) { + return new BlockPatternOr(this, pattern); + } + + @Override + public String getDisplayName() { + return block.getLocalizedName(); + } + + @Override + public String toString() { + return ""; + } + + private static String getBlockId(Block block) + { + return (String) Block.blockRegistry.getNameForObject(block); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCWorldBlock.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCWorldBlock.java new file mode 100644 index 0000000..c13553a --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/block/MCWorldBlock.java @@ -0,0 +1,96 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.block; + +import java.util.Collections; +import java.util.List; + +import minetweaker.api.block.BlockPatternOr; +import minetweaker.api.block.IBlock; +import minetweaker.api.block.IBlockDefinition; +import minetweaker.api.block.IBlockPattern; +import minetweaker.api.data.IData; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCWorldBlock implements IBlock { + private final IBlockAccess blocks; + private final int x; + private final int y; + private final int z; + private final BlockPos position; + + public MCWorldBlock(IBlockAccess blocks, int x, int y, int z) { + this.blocks = blocks; + this.x = x; + this.y = y; + this.z = z; + this.position = new BlockPos(x, y, z); + } + + @Override + public IBlockDefinition getDefinition() { + return MineTweakerMC.getBlockDefinition(blocks.getBlockState(position).getBlock()); + } + + @Override + public int getMeta() { + return blocks.getBlockState(position).getBlock().getMetaFromState(blocks.getBlockState(position)); + } + + @Override + public IData getTileData() { + TileEntity tileEntity = blocks.getTileEntity(position); + + if (tileEntity == null) + return null; + + NBTTagCompound nbt = new NBTTagCompound(); + tileEntity.writeToNBT(nbt); + return MineTweakerMC.getIData(nbt); + } + + @Override + public String getDisplayName() { + Block block = blocks.getBlockState(position).getBlock(); + Item item = Item.getItemFromBlock(block); + if (item != null) { + return (new ItemStack(item, 1, getMeta())).getDisplayName(); + } else { + return block.getLocalizedName(); + } + } + + @Override + public List getBlocks() { + return Collections.singletonList(this); + } + + @Override + public boolean matches(IBlock block) { + return getDefinition() == block.getDefinition() + && (getMeta() == OreDictionary.WILDCARD_VALUE || getMeta() == block.getMeta()) + && (getTileData() == null || (block.getTileData() != null && block.getTileData().contains(getTileData()))); + } + + @Override + public IBlockPattern or(IBlockPattern pattern) { + return new BlockPatternOr(this, pattern); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/ItemBracketHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/ItemBracketHandler.java new file mode 100644 index 0000000..6e4b031 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/ItemBracketHandler.java @@ -0,0 +1,142 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.brackets; + +import static minetweaker.api.minecraft.MineTweakerMC.getIItemStackWildcardSize; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import minetweaker.IBracketHandler; +import minetweaker.MineTweakerAPI; +import minetweaker.annotations.BracketHandler; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.IngredientAny; +import net.minecraft.item.Item; +import net.minecraftforge.oredict.OreDictionary; +import stanhebben.zenscript.ZenTokener; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.expression.ExpressionCallStatic; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.IJavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +@BracketHandler(priority = 100) +public class ItemBracketHandler implements IBracketHandler { + private static final Map itemNames; + + static { + itemNames = new HashMap(); + for (String itemName : (Set) Item.itemRegistry.getKeys()) { + itemNames.put(itemName.replace(" ", ""), (Item) Item.itemRegistry.getObject(itemName)); + System.out.println(itemName + " : " + (Item) Item.itemRegistry.getObject(itemName)); + + } + } + + public static IItemStack getItem(String name, int meta) { + // Item item = (Item) Item.itemRegistry.getObject(name); + Item item = itemNames.get(name); + if (item != null) { + return getIItemStackWildcardSize(item, meta); + } else { + return null; + } + } + + private final IZenSymbol symbolAny; + private final IJavaMethod method; + + public ItemBracketHandler() { + symbolAny = MineTweakerAPI.getJavaStaticFieldSymbol(IngredientAny.class, "INSTANCE"); + method = MineTweakerAPI.getJavaMethod( + ItemBracketHandler.class, + "getItem", + String.class, int.class); + } + + @Override + public IZenSymbol resolve(IEnvironmentGlobal environment, List tokens) { + // any symbol + if (tokens.size() == 1 && tokens.get(0).getValue().equals("*")) { + return symbolAny; + } + + // detect special cases: + // item: at the start means item-specific syntax + // :xxx with xxx an integer means sub-item syntax + // :* means any-subitem-syntax + int fromIndex = 0; + int toIndex = tokens.size(); + int meta = 0; + + if (tokens.size() > 2) { + if (tokens.get(0).getValue().equals("item") && tokens.get(1).getValue().equals(":")) { + fromIndex = 2; + } + if (tokens.get(tokens.size() - 1).getType() == ZenTokener.T_INTVALUE + && tokens.get(tokens.size() - 2).getValue().equals(":")) { + toIndex = tokens.size() - 2; + meta = Integer.parseInt(tokens.get(tokens.size() - 1).getValue()); + } else if (tokens.get(tokens.size() - 1).getValue().equals("*") + && tokens.get(tokens.size() - 2).getValue().equals(":")) { + toIndex = tokens.size() - 2; + meta = OreDictionary.WILDCARD_VALUE; + } + } + + return find(environment, tokens, fromIndex, toIndex, meta); + } + + private IZenSymbol find(IEnvironmentGlobal environment, List tokens, int startIndex, int endIndex, int meta) { + StringBuilder valueBuilder = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + Token token = tokens.get(i); + valueBuilder.append(token.getValue()); + } + + String itemName = valueBuilder.toString(); + if (itemNames.containsKey(itemName)) { + return new ItemReferenceSymbol(environment, itemName, meta); + } + + return null; + } + + private class ItemReferenceSymbol implements IZenSymbol { + private final IEnvironmentGlobal environment; + private final String name; + private final int meta; + + public ItemReferenceSymbol(IEnvironmentGlobal environment, String name, int meta) { + this.environment = environment; + this.name = name; + this.meta = meta; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new ExpressionCallStatic( + position, + environment, + method, + new ExpressionString(position, name), + new ExpressionInt(position, meta, ZenType.INT)); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/LiquidBracketHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/LiquidBracketHandler.java new file mode 100644 index 0000000..b2dedde --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/LiquidBracketHandler.java @@ -0,0 +1,88 @@ +package minetweaker.mc18.brackets; + +import java.util.List; + +import minetweaker.IBracketHandler; +import minetweaker.annotations.BracketHandler; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.mc18.liquid.MCLiquidStack; +import minetweaker.runtime.GlobalRegistry; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.expression.ExpressionCallStatic; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.natives.IJavaMethod; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +@BracketHandler +public class LiquidBracketHandler implements IBracketHandler { + public static ILiquidStack getLiquid(String name) { + Fluid fluid = FluidRegistry.getFluid(name); + if (fluid != null) { + return new MCLiquidStack(new FluidStack(fluid, 1)); + } else { + return null; + } + } + + @Override + public IZenSymbol resolve(IEnvironmentGlobal environment, List tokens) { + if (tokens.size() > 2) { + if (tokens.get(0).getValue().equals("liquid") && tokens.get(1).getValue().equals(":")) { + return find(environment, tokens, 2, tokens.size()); + } + } + + return null; + } + + private IZenSymbol find(IEnvironmentGlobal environment, List tokens, int startIndex, int endIndex) { + StringBuilder valueBuilder = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + Token token = tokens.get(i); + valueBuilder.append(token.getValue()); + } + + Fluid fluid = FluidRegistry.getFluid(valueBuilder.toString()); + if (fluid != null) { + return new LiquidReferenceSymbol(environment, valueBuilder.toString()); + } + + return null; + } + + private class LiquidReferenceSymbol implements IZenSymbol { + private final IEnvironmentGlobal environment; + private final String name; + + public LiquidReferenceSymbol(IEnvironmentGlobal environment, String name) { + this.environment = environment; + this.name = name; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + IJavaMethod method = JavaMethod.get( + GlobalRegistry.getTypeRegistry(), + LiquidBracketHandler.class, + "getLiquid", + String.class); + + return new ExpressionCallStatic( + position, + environment, + method, + new ExpressionString(position, name)); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/OreBracketHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/OreBracketHandler.java new file mode 100644 index 0000000..5b9ecd7 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/brackets/OreBracketHandler.java @@ -0,0 +1,81 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.brackets; + +import java.util.List; + +import minetweaker.IBracketHandler; +import minetweaker.annotations.BracketHandler; +import minetweaker.api.oredict.IOreDictEntry; +import minetweaker.mc18.oredict.MCOreDictEntry; +import minetweaker.runtime.GlobalRegistry; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.expression.ExpressionCallStatic; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.natives.IJavaMethod; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +@BracketHandler +public class OreBracketHandler implements IBracketHandler { + public static IOreDictEntry getOre(String name) { + return new MCOreDictEntry(name); + } + + @Override + public IZenSymbol resolve(IEnvironmentGlobal environment, List tokens) { + if (tokens.size() > 2) { + if (tokens.get(0).getValue().equals("ore") && tokens.get(1).getValue().equals(":")) { + return find(environment, tokens, 2, tokens.size()); + } + } + + return null; + } + + private IZenSymbol find(IEnvironmentGlobal environment, List tokens, int startIndex, int endIndex) { + StringBuilder valueBuilder = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + Token token = tokens.get(i); + valueBuilder.append(token.getValue()); + } + + return new OreReferenceSymbol(environment, valueBuilder.toString()); + } + + private class OreReferenceSymbol implements IZenSymbol { + private final IEnvironmentGlobal environment; + private final String name; + + public OreReferenceSymbol(IEnvironmentGlobal environment, String name) { + this.environment = environment; + this.name = name; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + IJavaMethod method = JavaMethod.get( + GlobalRegistry.getTypeRegistry(), + OreBracketHandler.class, + "getOre", + String.class); + + return new ExpressionCallStatic( + position, + environment, + method, + new ExpressionString(position, name)); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/chat/MCChatMessage.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/chat/MCChatMessage.java new file mode 100644 index 0000000..0e6d4ec --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/chat/MCChatMessage.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.chat; + +import minetweaker.api.chat.IChatMessage; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +/** + * + * @author Stan + */ +public class MCChatMessage implements IChatMessage { + private final IChatComponent data; + + public MCChatMessage(String message) { + data = new ChatComponentText(message); + } + + public MCChatMessage(IChatComponent data) { + this.data = data; + } + + @Override + public IChatMessage add(IChatMessage other) { + return new MCChatMessage(data.appendSibling((IChatComponent) other.getInternal())); + } + + @Override + public Object getInternal() { + return data; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/client/MCClient.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/client/MCClient.java new file mode 100644 index 0000000..feb3088 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/client/MCClient.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.client; + +import minetweaker.api.client.IClient; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.player.IPlayer; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.client.FMLClientHandler; + +/** + * + * @author Stan + */ +public class MCClient implements IClient { + @Override + public IPlayer getPlayer() { + return MineTweakerMC.getIPlayer(Minecraft.getMinecraft().thePlayer); + } + + @Override + public String getLanguage() { + return FMLClientHandler.instance().getCurrentLanguage(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTConverter.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTConverter.java new file mode 100644 index 0000000..d1e0270 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTConverter.java @@ -0,0 +1,172 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import minetweaker.api.data.DataByte; +import minetweaker.api.data.DataByteArray; +import minetweaker.api.data.DataDouble; +import minetweaker.api.data.DataFloat; +import minetweaker.api.data.DataInt; +import minetweaker.api.data.DataIntArray; +import minetweaker.api.data.DataList; +import minetweaker.api.data.DataLong; +import minetweaker.api.data.DataMap; +import minetweaker.api.data.DataShort; +import minetweaker.api.data.DataString; +import minetweaker.api.data.IData; +import minetweaker.api.data.IDataConverter; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; +import net.minecraft.nbt.NBTTagByteArray; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagDouble; +import net.minecraft.nbt.NBTTagFloat; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.nbt.NBTTagIntArray; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.nbt.NBTTagShort; +import net.minecraft.nbt.NBTTagString; + +/** + * + * @author Stan + */ +public class NBTConverter implements IDataConverter { + private static final NBTConverter INSTANCE = new NBTConverter(); + + public static NBTBase from(IData data) { + return data.convert(INSTANCE); + } + + public static IData from(NBTBase nbt, boolean immutable) { + if (nbt == null) + return null; + + switch (nbt.getId()) { + case 1: // byte + return new DataByte(((NBTBase.NBTPrimitive) nbt).getByte()); + case 2: // short + return new DataShort(((NBTBase.NBTPrimitive) nbt).getShort()); + case 3: // int + return new DataInt(((NBTBase.NBTPrimitive) nbt).getInt()); + case 4: // long + return new DataLong(((NBTBase.NBTPrimitive) nbt).getLong()); + case 5: // float + return new DataFloat(((NBTBase.NBTPrimitive) nbt).getFloat()); + case 6: // double + return new DataDouble(((NBTBase.NBTPrimitive) nbt).getDouble()); + case 7: // byte[] + return new DataByteArray(((NBTTagByteArray) nbt).getByteArray(), immutable); + case 8: // string + return new DataString(((NBTTagString) nbt).getString()); + case 9: { // list + List values = new ArrayList(); + List original = MineTweakerHacks.getTagList((NBTTagList) nbt); + for (NBTBase value : original) { + values.add(from(value, immutable)); + } + return new DataList(values, immutable); + } + case 10: { // compound + Map values = new HashMap(); + NBTTagCompound original = (NBTTagCompound) nbt; + for (String key : (Set) original.getKeySet()) { + values.put(key, from(original.getTag(key), immutable)); + } + return new DataMap(values, immutable); + } + case 11: // int[] + return new DataIntArray(((NBTTagIntArray) nbt).getIntArray(), immutable); + default: + throw new RuntimeException("Unknown tag type: " + nbt.getId()); + } + } + + public static void updateMap(NBTTagCompound nbt, IData data) { + NBTUpdater updater = new NBTUpdater(nbt); + data.convert(updater); + } + + public NBTConverter() { + } + + @Override + public NBTBase fromBool(boolean value) { + return new NBTTagInt(value ? 1 : 0); + } + + @Override + public NBTBase fromByte(byte value) { + return new NBTTagByte(value); + } + + @Override + public NBTBase fromShort(short value) { + return new NBTTagShort(value); + } + + @Override + public NBTBase fromInt(int value) { + return new NBTTagInt(value); + } + + @Override + public NBTBase fromLong(long value) { + return new NBTTagLong(value); + } + + @Override + public NBTBase fromFloat(float value) { + return new NBTTagFloat(value); + } + + @Override + public NBTBase fromDouble(double value) { + return new NBTTagDouble(value); + } + + @Override + public NBTBase fromString(String value) { + return new NBTTagString(value); + } + + @Override + public NBTBase fromList(List values) { + NBTTagList result = new NBTTagList(); + for (IData value : values) { + result.appendTag(from(value)); + } + return result; + } + + @Override + public NBTBase fromMap(Map values) { + NBTTagCompound result = new NBTTagCompound(); + for (Map.Entry entry : values.entrySet()) { + result.setTag(entry.getKey(), from(entry.getValue())); + } + return result; + } + + @Override + public NBTBase fromByteArray(byte[] value) { + return new NBTTagByteArray(value); + } + + @Override + public NBTBase fromIntArray(int[] value) { + return new NBTTagIntArray(value); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTUpdater.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTUpdater.java new file mode 100644 index 0000000..7c46069 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/data/NBTUpdater.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.data; + +import java.util.Map; + +import minetweaker.api.data.IData; +import minetweaker.api.data.IDataConverter; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; + +/** + * + * @author Stan + */ +public class NBTUpdater extends NBTConverter implements IDataConverter { + private final NBTTagCompound nbt; + + public NBTUpdater(NBTTagCompound compound) { + this.nbt = compound; + } + + @Override + public NBTBase fromMap(Map map) { + for (Map.Entry entry : map.entrySet()) { + if (nbt.hasKey(entry.getKey())) { + if (entry.getValue() == null) { + nbt.removeTag(entry.getKey()); + } else { + if (nbt.getTag(entry.getKey()).getId() == 10) { + NBTTagCompound tag = (NBTTagCompound) nbt.getTag(entry.getKey()); + nbt.setTag(entry.getKey(), entry.getValue().convert(new NBTUpdater(nbt))); + } else { + nbt.setTag(entry.getKey(), NBTConverter.from(entry.getValue())); + } + } + } else if (entry.getValue() != null) { + nbt.setTag(entry.getKey(), from(entry.getValue())); + } + } + + return nbt; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/entity/MCEntityDefinition.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/entity/MCEntityDefinition.java new file mode 100644 index 0000000..f366061 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/entity/MCEntityDefinition.java @@ -0,0 +1,32 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.entity; + +import minetweaker.api.entity.IEntityDefinition; +import net.minecraftforge.fml.common.registry.EntityRegistry; + +/** + * + * @author Stan + */ +public class MCEntityDefinition implements IEntityDefinition { + private final EntityRegistry.EntityRegistration registration; + + public MCEntityDefinition(EntityRegistry.EntityRegistration registration) { + this.registration = registration; + } + + @Override + public String getId() { + return registration.getEntityClass().getName(); + } + + @Override + public String getName() { + return registration.getEntityName(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedMarkupString.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedMarkupString.java new file mode 100644 index 0000000..b467a06 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedMarkupString.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.formatting; + +import minetweaker.api.formatting.IFormattedText; +import net.minecraft.util.EnumChatFormatting; + +/** + * + * @author Stan + */ +public class FormattedMarkupString implements IMCFormattedString { + private final EnumChatFormatting markup; + private final IMCFormattedString contents; + + public FormattedMarkupString(EnumChatFormatting markup, IMCFormattedString contents) { + this.markup = markup; + this.contents = contents; + } + + @Override + public String getTooltipString() { + return markup + contents.getTooltipString(markup.toString()); + } + + @Override + public String getTooltipString(String context) { + return markup + contents.getTooltipString(context + markup); + } + + @Override + public IFormattedText add(IFormattedText other) { + return cat(other); + } + + @Override + public IFormattedText cat(IFormattedText other) { + return new FormattedStringJoin(this, (IMCFormattedString) other); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedString.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedString.java new file mode 100644 index 0000000..be738e0 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedString.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.formatting; + +import minetweaker.api.formatting.IFormattedText; + +/** + * + * @author Stan + */ +public class FormattedString implements IMCFormattedString { + private final String value; + + public FormattedString(String value) { + this.value = value; + } + + @Override + public String getTooltipString() { + return value; + } + + @Override + public String getTooltipString(String context) { + return value; + } + + @Override + public IFormattedText add(IFormattedText other) { + return cat(other); + } + + @Override + public IFormattedText cat(IFormattedText other) { + return new FormattedStringJoin(this, (IMCFormattedString) other); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedStringJoin.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedStringJoin.java new file mode 100644 index 0000000..1452c14 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/FormattedStringJoin.java @@ -0,0 +1,68 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.formatting; + +import java.util.Arrays; + +import minetweaker.api.formatting.IFormattedText; +import net.minecraft.util.EnumChatFormatting; + +/** + * + * @author Stan + */ +public class FormattedStringJoin implements IMCFormattedString { + private final IMCFormattedString[] values; + + public FormattedStringJoin(IMCFormattedString first, IMCFormattedString second) { + values = new IMCFormattedString[] { first, second }; + } + + public FormattedStringJoin(IMCFormattedString[] values) { + this.values = values; + } + + @Override + public String getTooltipString() { + StringBuilder result = new StringBuilder(); + for (IMCFormattedString value : values) { + result.append(value.getTooltipString()); + } + return result.toString(); + } + + @Override + public String getTooltipString(String context) { + boolean first = true; + + StringBuilder result = new StringBuilder(); + for (IMCFormattedString value : values) { + if (first) { + first = false; + } else { + result.append(context); + } + + result.append(value.getTooltipString()); + result.append(EnumChatFormatting.RESET); + } + + return result.toString(); + } + + @Override + public IFormattedText add(IFormattedText other) { + return cat(other); + } + + @Override + public IFormattedText cat(IFormattedText other) { + IMCFormattedString[] newValues = Arrays.copyOf(values, values.length + 1); + newValues[values.length] = (IMCFormattedString) other; + return new FormattedStringJoin(newValues); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/IMCFormattedString.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/IMCFormattedString.java new file mode 100644 index 0000000..b91b71d --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/IMCFormattedString.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.formatting; + +import minetweaker.api.formatting.IFormattedText; + +/** + * + * @author Stan + */ +public interface IMCFormattedString extends IFormattedText { + public String getTooltipString(); + + public String getTooltipString(String context); +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/MCFormatter.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/MCFormatter.java new file mode 100644 index 0000000..57690a8 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/formatting/MCFormatter.java @@ -0,0 +1,128 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.formatting; + +import minetweaker.api.formatting.IFormattedText; +import minetweaker.api.formatting.IFormatter; +import net.minecraft.util.EnumChatFormatting; + +/** + * + * @author Stan + */ +public class MCFormatter implements IFormatter { + + @Override + public IFormattedText string(String text) { + return new FormattedString(text); + } + + @Override + public IFormattedText black(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.BLACK, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkBlue(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_BLUE, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkGreen(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_GREEN, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkAqua(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_AQUA, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkRed(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_RED, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkPurple(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_PURPLE, (IMCFormattedString) text); + } + + @Override + public IFormattedText gold(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.GOLD, (IMCFormattedString) text); + } + + @Override + public IFormattedText gray(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.GRAY, (IMCFormattedString) text); + } + + @Override + public IFormattedText darkGray(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.DARK_GRAY, (IMCFormattedString) text); + } + + @Override + public IFormattedText blue(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.BLUE, (IMCFormattedString) text); + } + + @Override + public IFormattedText green(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.GREEN, (IMCFormattedString) text); + } + + @Override + public IFormattedText aqua(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.AQUA, (IMCFormattedString) text); + } + + @Override + public IFormattedText red(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.RED, (IMCFormattedString) text); + } + + @Override + public IFormattedText lightPurple(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.LIGHT_PURPLE, (IMCFormattedString) text); + } + + @Override + public IFormattedText yellow(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.YELLOW, (IMCFormattedString) text); + } + + @Override + public IFormattedText white(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.WHITE, (IMCFormattedString) text); + } + + @Override + public IFormattedText obfuscated(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.OBFUSCATED, (IMCFormattedString) text); + } + + @Override + public IFormattedText bold(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.BOLD, (IMCFormattedString) text); + } + + @Override + public IFormattedText strikethrough(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.STRIKETHROUGH, (IMCFormattedString) text); + } + + @Override + public IFormattedText underline(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.UNDERLINE, (IMCFormattedString) text); + } + + @Override + public IFormattedText italic(IFormattedText text) { + return new FormattedMarkupString(EnumChatFormatting.ITALIC, (IMCFormattedString) text); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/FuelTweaker.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/FuelTweaker.java new file mode 100644 index 0000000..9809536 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/FuelTweaker.java @@ -0,0 +1,95 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.furnace; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.common.IFuelHandler; +import net.minecraftforge.fml.common.registry.GameRegistry; + +public class FuelTweaker { + public static final FuelTweaker INSTANCE = new FuelTweaker(); + + private List original; + private final HashMap> quickList = new HashMap>(); + + private FuelTweaker() { + } + + public void register() { + try { + Field fuelHandlers = GameRegistry.class.getDeclaredField("fuelHandlers"); + fuelHandlers.setAccessible(true); + original = (List) fuelHandlers.get(null); + List modified = new ArrayList(); + modified.add(new OverridingFuelHandler()); + fuelHandlers.set(null, modified); + } catch (NoSuchFieldException ex) { + System.out.println("[MineTweaker] Error: could not get GameRegistry fuel handlers field. Cannot use custom fuel values."); + } catch (SecurityException ex) { + System.out.println("[MineTweaker] Error: could not alter GameRegistry fuel handlers field. Cannot use custom fuel values."); + } catch (IllegalAccessException ex) { + System.out.println("[MineTweaker] Error: could not alter GameRegistry fuel handlers field. Cannot use custom fuel values."); + } + } + + public void addFuelPattern(SetFuelPattern pattern) { + List items = pattern.getPattern().getItems(); + if (items == null) { + MineTweakerAPI.logError("Cannot set fuel for <*>"); + return; + } + + for (IItemStack item : pattern.getPattern().getItems()) { + ItemStack itemStack = MineTweakerMC.getItemStack(item); + Item mcItem = itemStack.getItem(); + if (!quickList.containsKey(mcItem)) { + quickList.put(mcItem, new ArrayList()); + } + quickList.get(mcItem).add(pattern); + } + } + + public void removeFuelPattern(SetFuelPattern pattern) { + for (IItemStack item : pattern.getPattern().getItems()) { + ItemStack itemStack = MineTweakerMC.getItemStack(item); + Item mcItem = itemStack.getItem(); + if (quickList.containsKey(mcItem)) { + quickList.get(mcItem).remove(pattern); + } + } + } + + private class OverridingFuelHandler implements IFuelHandler { + @Override + public int getBurnTime(ItemStack fuel) { + if (quickList.containsKey(fuel.getItem())) { + IItemStack stack = MineTweakerMC.getIItemStack(fuel); + + for (SetFuelPattern override : quickList.get(fuel.getItem())) { + if (override.getPattern().matches(stack)) { + return override.getValue(); + } + } + } + + int max = 0; + for (IFuelHandler handler : original) { + max = Math.max(max, handler.getBurnTime(fuel)); + } + return max; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/MCFurnaceManager.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/MCFurnaceManager.java new file mode 100644 index 0000000..5f8f93e --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/MCFurnaceManager.java @@ -0,0 +1,214 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.furnace; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import static minetweaker.api.minecraft.MineTweakerMC.getItemStacks; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemStack; +import minetweaker.api.recipes.IFurnaceManager; +import minetweaker.mc18.item.MCItemStack; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraftforge.fml.common.registry.GameRegistry; + +/** + * + * @author Stan + */ +public class MCFurnaceManager implements IFurnaceManager { + public MCFurnaceManager() { + + } + + @Override + public void remove(IIngredient output, IIngredient input) { + if (output == null) + throw new IllegalArgumentException("output cannot be null"); + + Map smeltingList = FurnaceRecipes.instance().getSmeltingList(); + + List toRemove = new ArrayList(); + List toRemoveValues = new ArrayList(); + for (Map.Entry entry : smeltingList.entrySet()) { + if (output.matches(new MCItemStack(entry.getValue())) + && (input == null || input.matches(new MCItemStack(entry.getKey())))) { + toRemove.add(entry.getKey()); + toRemoveValues.add(entry.getValue()); + } + } + + if (toRemove.isEmpty()) { + MineTweakerAPI.logWarning("No furnace recipes for " + output.toString()); + } else { + MineTweakerAPI.apply(new RemoveAction(toRemove, toRemoveValues)); + } + } + + @Override + public void addRecipe(IItemStack output, IIngredient input, double xp) { + List items = input.getItems(); + if (items == null) { + MineTweakerAPI.logError("Cannot turn " + input.toString() + " into a furnace recipe"); + } + + ItemStack[] items2 = getItemStacks(items); + ItemStack output2 = getItemStack(output); + MineTweakerAPI.apply(new AddRecipeAction(input, items2, output2, xp)); + } + + @Override + public void setFuel(IIngredient item, int fuel) { + MineTweakerAPI.apply(new SetFuelAction(new SetFuelPattern(item, fuel))); + } + + @Override + public int getFuel(IItemStack item) { + return GameRegistry.getFuelValue(getItemStack(item)); + } + + // ###################### + // ### Action classes ### + // ###################### + + private static class RemoveAction implements IUndoableAction { + private final List items; + private final List values; + + public RemoveAction(List items, List values) { + this.items = items; + this.values = values; + } + + @Override + public void apply() { + for (ItemStack item : items) { + FurnaceRecipes.instance().getSmeltingList().remove(item); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (int i = 0; i < items.size(); i++) { + FurnaceRecipes.instance().getSmeltingList().put(items.get(i), values.get(i)); + } + } + + @Override + public String describe() { + return "Removing " + items.size() + " furnace recipes"; + } + + @Override + public String describeUndo() { + return "Restoring " + items.size() + " furnace recipes"; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class AddRecipeAction implements IUndoableAction { + private final IIngredient ingredient; + private final ItemStack[] input; + private final ItemStack output; + private final double xp; + + public AddRecipeAction(IIngredient ingredient, ItemStack[] input, ItemStack output, double xp) { + this.ingredient = ingredient; + this.input = input; + this.output = output; + this.xp = xp; + } + + @Override + public void apply() { + for (ItemStack inputStack : input) { + FurnaceRecipes.instance().addSmeltingRecipe(inputStack, output, (float) xp); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (ItemStack inputStack : input) { + FurnaceRecipes.instance().getSmeltingList().remove(inputStack); + } + } + + @Override + public String describe() { + return "Adding furnace recipe for " + ingredient; + } + + @Override + public String describeUndo() { + return "Removing furnace recipe for " + ingredient; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class SetFuelAction implements IUndoableAction { + private final SetFuelPattern pattern; + + public SetFuelAction(SetFuelPattern pattern) { + this.pattern = pattern; + } + + @Override + public void apply() { + FuelTweaker.INSTANCE.addFuelPattern(pattern); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + FuelTweaker.INSTANCE.removeFuelPattern(pattern); + } + + @Override + public String describe() { + return "Setting fuel for " + pattern.getPattern(); + } + + @Override + public String describeUndo() { + return "Removing fuel for " + pattern.getPattern(); + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/SetFuelPattern.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/SetFuelPattern.java new file mode 100644 index 0000000..31d8098 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/furnace/SetFuelPattern.java @@ -0,0 +1,21 @@ +package minetweaker.mc18.furnace; + +import minetweaker.api.item.IIngredient; + +public class SetFuelPattern { + private final IIngredient pattern; + private final int value; + + public SetFuelPattern(IIngredient pattern, int value) { + this.pattern = pattern; + this.value = value; + } + + public IIngredient getPattern() { + return pattern; + } + + public int getValue() { + return value; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/game/MCGame.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/game/MCGame.java new file mode 100644 index 0000000..ae134fa --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/game/MCGame.java @@ -0,0 +1,214 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.game; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.block.IBlockDefinition; +import minetweaker.api.entity.IEntityDefinition; +import minetweaker.api.game.IGame; +import minetweaker.api.item.IItemDefinition; +import minetweaker.api.liquid.ILiquidDefinition; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.world.IBiome; +// TODO Find this +// import minetweaker.mc1710.GuiCannotRemodify; +import minetweaker.mc18.entity.MCEntityDefinition; +import minetweaker.mc18.item.MCItemDefinition; +import minetweaker.mc18.liquid.MCLiquidDefinition; +import minetweaker.mc18.util.MineTweakerHacks; +import minetweaker.mc18.util.MineTweakerPlatformUtils; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.item.Item; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fml.common.registry.EntityRegistry; +import net.minecraftforge.fml.common.registry.LanguageRegistry; + +/** + * + * @author Stan + */ +public class MCGame implements IGame { + private static final Map TRANSLATIONS = MineTweakerHacks.getTranslations(); + + public static final MCGame INSTANCE = new MCGame(); + + private boolean locked = false; + + private MCGame() { + } + + @Override + public List getItems() { + List result = new ArrayList(); + for (String item : (Set) Item.itemRegistry.getKeys()) { + result.add(new MCItemDefinition(item, (Item) Item.itemRegistry.getObject(item))); + } + return result; + } + + @Override + public List getBlocks() { + List result = new ArrayList(); + for (String block : (Set) Block.blockRegistry.getKeys()) { + result.add(MineTweakerMC.getBlockDefinition((Block) Block.blockRegistry.getObject(block))); + } + + return result; + } + + @Override + public List getLiquids() { + List result = new ArrayList(); + for (Map.Entry entry : FluidRegistry.getRegisteredFluids().entrySet()) { + result.add(new MCLiquidDefinition(entry.getValue())); + } + return result; + } + + @Override + public List getBiomes() { + List result = new ArrayList(); + for (IBiome biome : MineTweakerMC.biomes) { + if (biome != null) { + result.add(biome); + } + } + return result; + } + + @Override + public List getEntities() { + List result = new ArrayList(); + + for (EntityRegistry.EntityRegistration entityRegistration : MineTweakerHacks.getEntityClassRegistrations().values()) { + result.add(new MCEntityDefinition(entityRegistration)); + } + + return result; + } + + @Override + public void setLocalization(String key, String value) { + MineTweakerAPI.apply(new SetTranslation(null, key, value)); + } + + @Override + public void setLocalization(String lang, String key, String value) { + MineTweakerAPI.apply(new SetTranslation(lang, key, value)); + } + + @Override + public String localize(String key) { + return LanguageRegistry.instance().getStringLocalization(key); + } + + @Override + public String localize(String key, String lang) { + return LanguageRegistry.instance().getStringLocalization(key, lang); + } + + @Override + public void lock() + { + locked = true; + } + + @Override + public boolean isLocked() + { + return locked; + } + + @Override + public void signalLockError() + { + MineTweakerAPI.getLogger().logError("Reload of scripts blocked (script lock)"); + + if (Minecraft.isGuiEnabled()) { + // Commented out due to unresolved import + /** + * Minecraft.getMinecraft().displayGuiScreen( new GuiCannotRemodify( + * "Minecraft has been tweaked for another server", + * "with modifications that cannot be rolled back.", + * "Please restart your game.")); + **/ + } + } + + // ###################### + // ### Action classes ### + // ###################### + + /** + * Ported from ModTweaker. + * + * @author Joshiejack + */ + private static class SetTranslation implements IUndoableAction { + private String original; + private final String lang; + private final String key; + private final String text; + private boolean added; + + public SetTranslation(String lang, String key, String text) { + this.lang = lang; + this.key = key; + this.text = text; + } + + @Override + public void apply() { + if (lang == null || MineTweakerPlatformUtils.isLanguageActive(lang)) { + original = TRANSLATIONS.get(key); + TRANSLATIONS.put(key, text); + added = true; + } else { + added = false; + } + } + + @Override + public boolean canUndo() { + return TRANSLATIONS != null; + } + + @Override + public void undo() { + if (added) { + if (original == null) { + TRANSLATIONS.remove(key); + } else { + TRANSLATIONS.put(key, original); + } + } + } + + @Override + public String describe() { + return "Setting localization for the key: " + key + " to " + text; + } + + @Override + public String describeUndo() { + return "Setting localization for the key: " + key + " to " + original; + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemDefinition.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemDefinition.java new file mode 100644 index 0000000..fa843e0 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemDefinition.java @@ -0,0 +1,90 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.item; + +import java.util.ArrayList; +import java.util.List; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IItemDefinition; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.oredict.IOreDictEntry; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCItemDefinition implements IItemDefinition { + private final String id; + private final Item item; + + public MCItemDefinition(String id, Item item) { + this.id = id; + this.item = item; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return item.getUnlocalizedName(); + } + + @Override + public IItemStack makeStack(int meta) { + return MineTweakerMC.getIItemStackWildcardSize(new ItemStack(item, 1, meta)); + } + + @Override + public List getOres() { + List result = new ArrayList(); + + for (String key : OreDictionary.getOreNames()) { + for (ItemStack is : OreDictionary.getOres(key)) { + if (is.getItem() == item) { + result.add(MineTweakerAPI.oreDict.get(key)); + break; + } + } + } + + return result; + } + + // ############################# + // ### Object implementation ### + // ############################# + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + (this.item != null ? this.item.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MCItemDefinition other = (MCItemDefinition) obj; + if (this.item != other.item && (this.item == null || !this.item.equals(other.item))) { + return false; + } + return true; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemStack.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemStack.java new file mode 100644 index 0000000..69d337b --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/item/MCItemStack.java @@ -0,0 +1,391 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.item; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.block.IBlock; +import minetweaker.api.data.DataMap; +import minetweaker.api.data.IData; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemCondition; +import minetweaker.api.item.IItemDefinition; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.IItemTransformer; +import minetweaker.api.item.IngredientItem; +import minetweaker.api.item.IngredientOr; +import minetweaker.api.item.WeightedItemStack; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.api.oredict.IOreDictEntry; +import minetweaker.api.player.IPlayer; +import minetweaker.mc18.actions.SetBlockHardnessAction; +import minetweaker.mc18.actions.SetStackSizeAction; +import minetweaker.mc18.actions.SetTranslationAction; +import minetweaker.mc18.block.MCItemBlock; +import minetweaker.mc18.data.NBTConverter; +import minetweaker.mc18.liquid.MCLiquidStack; +import minetweaker.util.ArrayUtil; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCItemStack implements IItemStack { + private final ItemStack stack; + private final List items; + private IData tag = null; + private boolean wildcardSize; + + public MCItemStack(ItemStack itemStack) { + if (itemStack == null) + throw new IllegalArgumentException("stack cannot be null"); + + stack = itemStack.copy(); + items = Collections.singletonList(this); + } + + public MCItemStack(ItemStack itemStack, boolean wildcardSize) { + this(itemStack); + + this.wildcardSize = wildcardSize; + } + + private MCItemStack(ItemStack itemStack, IData tag) { + if (itemStack == null) + throw new IllegalArgumentException("stack cannot be null"); + + stack = itemStack; + items = Collections.singletonList(this); + this.tag = tag; + } + + private MCItemStack(ItemStack itemStack, IData tag, boolean wildcardSize) { + stack = itemStack; + items = Collections.singletonList(this); + this.tag = tag; + this.wildcardSize = wildcardSize; + } + + @Override + public IItemDefinition getDefinition() { + return new MCItemDefinition((String) Item.itemRegistry.getNameForObject(stack.getItem()), stack.getItem()); + } + + @Override + public String getName() { + return stack.getUnlocalizedName(); + } + + @Override + public String getDisplayName() { + return stack.getDisplayName(); + } + + @Override + public void setDisplayName(String name) { + MineTweakerAPI.apply(new SetTranslationAction(getName() + ".name", name)); + } + + @Override + public int getMaxStackSize() { + return stack.getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int size) { + MineTweakerAPI.apply(new SetStackSizeAction((ItemStack) getInternal(), size)); + } + + @Override + public float getBlockHardness() { + return ReflectionHelper.getPrivateValue(Block.class, Block.getBlockFromItem(stack.getItem()), "blockHardness"); + } + + @Override + public void setBlockHardness(float hardness) { + MineTweakerAPI.apply(new SetBlockHardnessAction(stack, hardness)); + } + + @Override + public int getDamage() { + return stack.getItemDamage(); + } + + @Override + public IData getTag() { + if (tag == null) { + if (stack.getTagCompound() == null) { + return DataMap.EMPTY; + } + + tag = NBTConverter.from(stack.getTagCompound(), true); + } + return tag; + } + + @Override + public int getMaxDamage() { + return stack.getMaxDamage(); + } + + @Override + public ILiquidStack getLiquid() { + FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(stack); + return liquid == null ? null : new MCLiquidStack(liquid); + } + + @Override + public IIngredient anyDamage() { + if (stack.getItem().getHasSubtypes()) { + MineTweakerAPI.logWarning("subitems don't have damaged states"); + return this; + } else { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, OreDictionary.WILDCARD_VALUE); + result.setTagCompound(stack.getTagCompound()); + return new MCItemStack(result, tag); + } + } + + @Override + public IItemStack withDamage(int damage) { + if (stack.getItem().getHasSubtypes()) { + MineTweakerAPI.logWarning("subitems don't have damaged states"); + return this; + } else { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, damage); + result.setTagCompound(stack.getTagCompound()); + return new MCItemStack(result, tag); + } + } + + @Override + public IItemStack anyAmount() { + ItemStack result = new ItemStack(stack.getItem(), 1, stack.getItemDamage()); + result.setTagCompound(stack.getTagCompound()); + return new MCItemStack(result, tag, true); + } + + @Override + public IItemStack withAmount(int amount) { + ItemStack result = new ItemStack(stack.getItem(), amount, stack.getItemDamage()); + result.setTagCompound(stack.getTagCompound()); + return new MCItemStack(result, tag); + } + + @Override + public IItemStack withTag(IData tag) { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, stack.getItemDamage()); + if (tag == null) { + result.setTagCompound(null); + } else { + result.setTagCompound((NBTTagCompound) NBTConverter.from(tag)); + } + return new MCItemStack(result, tag); + } + + @Override + public IItemStack updateTag(IData tagUpdate) { + if (tag == null) { + if (stack.getTagCompound() == null) { + return withTag(tagUpdate); + } + + tag = NBTConverter.from(stack.getTagCompound(), true); + } + + IData updated = tag.update(tagUpdate); + return withTag(updated); + } + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return stack.stackSize; + } + + @Override + public List getItems() { + return items; + } + + @Override + public List getLiquids() { + return Collections.emptyList(); + } + + @Override + public IItemStack amount(int amount) { + return withAmount(amount); + } + + @Override + public WeightedItemStack percent(float chance) { + return new WeightedItemStack(this, chance * 0.01f); + } + + @Override + public WeightedItemStack weight(float chance) { + return new WeightedItemStack(this, chance); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientItem(this, null, ArrayUtil.EMPTY_CONDITIONS, new IItemTransformer[] { transformer }); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientItem(this, null, new IItemCondition[] { condition }, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientItem(this, mark, ArrayUtil.EMPTY_CONDITIONS, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient or(IIngredient ingredient) { + return new IngredientOr(this, ingredient); + } + + @Override + public boolean matches(IItemStack item) { + ItemStack internal = getItemStack(item); + return internal.getItem() == stack.getItem() && (wildcardSize || internal.stackSize >= stack.stackSize) && (stack.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack.getItemDamage() == internal.getItemDamage() || (!stack.getHasSubtypes() && !stack.getItem().isDamageable())); + } + + @Override + public boolean matches(ILiquidStack liquid) { + return false; + } + + @Override + public boolean contains(IIngredient ingredient) { + List iitems = ingredient.getItems(); + if (iitems == null || iitems.size() != 1) + return false; + return matches(iitems.get(0)); + } + + @Override + public IItemStack applyTransform(IItemStack item, IPlayer byPlayer) { + return item; + } + + @Override + public boolean hasTransformers() { + return false; + } + + @Override + public Object getInternal() { + return stack; + } + + @Override + public IBlock asBlock() { + String name = (String) Block.blockRegistry.getNameForObject(Block.getBlockFromItem(stack.getItem())); + if (Block.blockRegistry.containsKey(name)) { + return new MCItemBlock(stack); + } else { + throw new ClassCastException("This item is not a block"); + } + } + + @Override + public List getOres() { + List result = new ArrayList(); + + for (String key : OreDictionary.getOreNames()) { + for (ItemStack is : OreDictionary.getOres(key)) { + if (is.getItem() == stack.getItem() && (is.getItemDamage() == OreDictionary.WILDCARD_VALUE || is.getItemDamage() == stack.getItemDamage())) { + result.add(MineTweakerAPI.oreDict.get(key)); + break; + } + } + } + + return result; + } + + // ############################# + // ### Object implementation ### + // ############################# + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + stack.getItem().hashCode(); + hash = 41 * hash + stack.getItemDamage(); + hash = 41 * hash + stack.stackSize; + hash = 41 * hash + (stack.getTagCompound() == null ? 0 : stack.getTagCompound().hashCode()); + hash = 41 * hash + (this.wildcardSize ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MCItemStack other = (MCItemStack) obj; + if (this.stack.getItem() != other.stack.getItem()) + return false; + if (this.stack.getItemDamage() != other.stack.getItemDamage()) + return false; + if (this.stack.stackSize != other.stack.stackSize) + return false; + if (this.stack.getTagCompound() != other.stack.getTagCompound() && (this.stack == null || this.stack.equals(other.stack))) + return false; + if (this.wildcardSize != other.wildcardSize) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append('<'); + result.append(Item.itemRegistry.getNameForObject(stack.getItem())); + + if (stack.getItemDamage() == OreDictionary.WILDCARD_VALUE) { + result.append(":*"); + } else if (stack.getItemDamage() > 0) { + result.append(':').append(stack.getItemDamage()); + } + result.append('>'); + + if (stack.getTagCompound() != null) { + result.append(".withTag("); + result.append(NBTConverter.from(stack.getTagCompound(), wildcardSize).toString()); + result.append(")"); + } + + return result.toString(); + } + +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidDefinition.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidDefinition.java new file mode 100644 index 0000000..4499471 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidDefinition.java @@ -0,0 +1,424 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.liquid; + +import java.util.ArrayList; +import java.util.List; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IItemStack; +import minetweaker.api.liquid.ILiquidDefinition; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; + +/** + * + * @author Stan + */ +public class MCLiquidDefinition implements ILiquidDefinition { + private final Fluid fluid; + + public MCLiquidDefinition(Fluid fluid) { + this.fluid = fluid; + } + + @Override + public String getName() { + return fluid.getName(); + } + + @Override + public String getDisplayName() { + return fluid.getLocalizedName(); + } + + @Override + public ILiquidStack asStack(int millibuckets) { + return new MCLiquidStack(new FluidStack(fluid, millibuckets)); + } + + @Override + public int getLuminosity() { + return fluid.getLuminosity(); + } + + @Override + public void setLuminosity(int value) { + MineTweakerAPI.apply(new ActionSetLuminosity(value)); + } + + @Override + public int getDensity() { + return fluid.getDensity(); + } + + @Override + public void setDensity(int density) { + MineTweakerAPI.apply(new ActionSetDensity(density)); + } + + @Override + public int getTemperature() { + return fluid.getTemperature(); + } + + @Override + public void setTemperature(int temperature) { + MineTweakerAPI.apply(new ActionSetTemperature(temperature)); + } + + @Override + public int getViscosity() { + return fluid.getViscosity(); + } + + @Override + public void setViscosity(int viscosity) { + MineTweakerAPI.apply(new ActionSetViscosity(viscosity)); + } + + @Override + public boolean isGaseous() { + return fluid.isGaseous(); + } + + @Override + public void setGaseous(boolean gaseous) { + MineTweakerAPI.apply(new ActionSetGaseous(gaseous)); + } + + @Override + public List getContainers() { + List result = new ArrayList(); + for (FluidContainerRegistry.FluidContainerData data : FluidContainerRegistry.getRegisteredFluidContainerData()) { + if (data.fluid.getFluid() == fluid) { + result.add(MineTweakerMC.getIItemStack(data.filledContainer)); + } + } + return result; + } + + @Override + public void addContainer(IItemStack filled, IItemStack empty, int amount) { + MineTweakerAPI.apply(new AddContainerAction(filled, empty, amount)); + } + + @Override + public void removeContainer(IItemStack filled) { + // MineTweakerAPI.apply(new RemoveContainerAction(filled)); + MineTweakerAPI.logError("Cannot remove container items in MineCraft 1.7.X"); + } + + // ####################### + // ### Private methods ### + // ####################### + + /* + * private void removeContainerInner(IItemStack filled) { ItemStack + * filledItem = MineTweakerMC.getItemStack(filled); + * FluidContainerRegistry.FluidContainerData data = + * filledContainerMap.get(Arrays.asList(filledItem.itemID, + * filledItem.getItemDamage())); if (data != null) { + * filledContainerMap.remove(Arrays.asList(filledItem.itemID, + * filledItem.getItemDamage())); + * containerFluidMap.remove(Arrays.asList(data.emptyContainer.itemID, + * data.emptyContainer.getItemDamage())); + * + * // rebuild empty containers set emptyContainers.clear(); for + * (FluidContainerRegistry.FluidContainerData fdata : + * filledContainerMap.values()) { + * emptyContainers.add(Arrays.asList(fdata.emptyContainer.itemID, + * fdata.emptyContainer.getItemDamage())); } } + * MineTweakerAPI.logError("Removal of containers is not possible in 1.7.X" + * ); } + */ + + /* + * private FluidContainerRegistry.FluidContainerData getData(IItemStack + * filled) { ItemStack filledStack = MineTweakerMC.getItemStack(filled); + * return filledContainerMap.get(Arrays.asList(filledStack.itemID, + * filledStack.getItemDamage())); } + */ + + // ###################### + // ### Action classes ### + // ###################### + + private class AddContainerAction implements IUndoableAction { + private final IItemStack filled; + private final IItemStack empty; + private final int amount; + + public AddContainerAction(IItemStack filled, IItemStack empty, int amount) { + this.filled = filled; + this.empty = empty; + this.amount = amount; + } + + @Override + public void apply() { + FluidContainerRegistry.registerFluidContainer(new FluidStack(fluid, amount), MineTweakerMC.getItemStack(filled), MineTweakerMC.getItemStack(empty)); + } + + @Override + public boolean canUndo() { + return false; + // return true; + } + + @Override + public void undo() { + // removeContainerInner(filled); + } + + @Override + public String describe() { + return "Adding " + filled.getDisplayName() + " as liquid container for " + fluid.getLocalizedName(); + } + + @Override + public String describeUndo() { + return "Removing liquid container " + filled; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + /* + * private class RemoveContainerAction implements IUndoableAction { private + * final IItemStack filled; private final IItemStack empty; private final + * FluidStack amount; + * + * public RemoveContainerAction(IItemStack filled) { this.filled = filled; + * + * FluidContainerRegistry.FluidContainerData data = getData(filled); empty = + * MineTweakerMC.getIItemStack(data.emptyContainer); amount = data.fluid; } + * + * @Override public void apply() { removeContainerInner(filled); } + * + * @Override public boolean canUndo() { return true; } + * + * @Override public void undo() { + * FluidContainerRegistry.registerFluidContainer(amount, + * MineTweakerMC.getItemStack(filled), MineTweakerMC.getItemStack(empty)); } + * + * @Override public String describe() { return "Removing liquid container " + * + filled; } + * + * @Override public String describeUndo() { return + * "Restoring liquid container " + filled; } + * + * @Override public Object getOverrideKey() { return null; } } + */ + + private class ActionSetLuminosity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetLuminosity(int newValue) { + oldValue = getLuminosity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setLuminosity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setLuminosity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " luminosity to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " luminosity to " + oldValue; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class ActionSetDensity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetDensity(int newValue) { + oldValue = getDensity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setDensity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setDensity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " density to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " density to " + oldValue; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class ActionSetTemperature implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetTemperature(int newValue) { + oldValue = getTemperature(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setTemperature(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setTemperature(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " temperature to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " temperature to " + oldValue; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class ActionSetViscosity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetViscosity(int newValue) { + oldValue = getViscosity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setViscosity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setViscosity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " viscosity to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " viscosity to " + oldValue; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class ActionSetGaseous implements IUndoableAction { + private final boolean oldValue; + private final boolean newValue; + + public ActionSetGaseous(boolean newValue) { + oldValue = isGaseous(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setGaseous(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setGaseous(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " gaseous to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " gaseous to " + oldValue; + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidStack.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidStack.java new file mode 100644 index 0000000..6b044ee --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/liquid/MCLiquidStack.java @@ -0,0 +1,210 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.liquid; + +import java.util.Collections; +import java.util.List; + +import minetweaker.api.data.IData; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemCondition; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.IItemTransformer; +import minetweaker.api.item.IngredientOr; +import minetweaker.api.liquid.ILiquidDefinition; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.api.player.IPlayer; +import minetweaker.mc18.data.NBTConverter; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +/** + * + * @author Stan + */ +public class MCLiquidStack implements ILiquidStack { + private final FluidStack stack; + private IData tag = null; + + public MCLiquidStack(FluidStack stack) { + this.stack = stack; + } + + private MCLiquidStack(FluidStack stack, IData tag) { + this.stack = stack; + this.tag = tag; + } + + @Override + public ILiquidDefinition getDefinition() { + return new MCLiquidDefinition(stack.getFluid()); + } + + @Override + public String getName() { + return stack.getFluid().getName(); + } + + @Override + public String getDisplayName() { + return stack.getFluid().getLocalizedName(); + } + + @Override + public int getAmount() { + return stack.amount; + } + + @Override + public IData getTag() { + if (tag == null) { + if (stack.tag == null) { + return null; + } + + tag = NBTConverter.from(stack.tag, true); + } + + return tag; + } + + @Override + public ILiquidStack withTag(IData data) { + FluidStack result = new FluidStack(stack.getFluid(), stack.amount); + result.tag = (NBTTagCompound) NBTConverter.from(data); + return new MCLiquidStack(result, data.immutable()); + } + + @Override + public ILiquidStack withAmount(int amount) { + FluidStack result = new FluidStack(stack.getFluid(), amount); + result.tag = stack.tag; + return new MCLiquidStack(result, tag); + } + + @Override + public int getLuminosity() { + return stack.getFluid().getLuminosity(stack); + } + + @Override + public int getDensity() { + return stack.getFluid().getDensity(stack); + } + + @Override + public int getTemperature() { + return stack.getFluid().getTemperature(stack); + } + + @Override + public int getViscosity() { + return stack.getFluid().getViscosity(stack); + } + + @Override + public boolean isGaseous() { + return stack.getFluid().isGaseous(stack); + } + + @Override + public Object getInternal() { + return stack; + } + + // ################################## + // ### IIngredient implementation ### + // ################################## + + @Override + public String getMark() + { + return null; + } + + @Override + public List getItems() + { + return Collections.emptyList(); + } + + @Override + public List getLiquids() + { + return Collections.singletonList(this); + } + + @Override + public IIngredient amount(int amount) + { + return withAmount(amount); + } + + @Override + public IIngredient or(IIngredient ingredient) + { + return new IngredientOr(this, ingredient); + } + + @Override + public IIngredient transform(IItemTransformer transformer) + { + throw new UnsupportedOperationException("Liquid stack can't have transformers"); + } + + @Override + public IIngredient only(IItemCondition condition) + { + throw new UnsupportedOperationException("Liquid stack can't have conditions"); + } + + @Override + public IIngredient marked(String mark) + { + throw new UnsupportedOperationException("Liquid stack can't be marked"); + } + + @Override + public boolean matches(IItemStack item) + { + return false; + } + + @Override + public boolean matches(ILiquidStack liquid) + { + return getDefinition().equals(liquid.getDefinition()) + && getAmount() <= liquid.getAmount(); + } + + @Override + public boolean contains(IIngredient ingredient) + { + if (!ingredient.getItems().isEmpty()) + return false; + + for (ILiquidStack liquid : ingredient.getLiquids()) + { + if (!matches(liquid)) + return false; + } + + return false; + } + + @Override + public IItemStack applyTransform(IItemStack item, IPlayer byPlayer) + { + return item; + } + + @Override + public boolean hasTransformers() + { + return false; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCLoadedMods.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCLoadedMods.java new file mode 100644 index 0000000..b9c0c64 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCLoadedMods.java @@ -0,0 +1,46 @@ +package minetweaker.mc18.mods; + +import java.util.Iterator; +import java.util.List; + +import minetweaker.api.mods.ILoadedMods; +import minetweaker.api.mods.IMod; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import stanhebben.zenscript.util.MappingIterator; + +/** + * + * @author Stan + */ +public class MCLoadedMods implements ILoadedMods { + public MCLoadedMods() { + } + + @Override + public boolean contains(String name) { + return Loader.isModLoaded(name); + } + + @Override + public IMod get(String name) { + for (ModContainer mod : Loader.instance().getActiveModList()) { + if (mod.getModId().equals(name)) { + return new MCMod(mod); + } + } + + return null; + } + + @Override + public Iterator iterator() { + List mods = Loader.instance().getActiveModList(); + return new MappingIterator(mods.iterator()) { + @Override + public IMod map(ModContainer value) { + return new MCMod(value); + } + }; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCMod.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCMod.java new file mode 100644 index 0000000..344f8e1 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/mods/MCMod.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.mods; + +import minetweaker.api.mods.IMod; +import net.minecraftforge.fml.common.ModContainer; + +/** + * + * @author Stan + */ +public class MCMod implements IMod { + private final ModContainer mod; + + public MCMod(ModContainer mod) { + this.mod = mod; + } + + @Override + public String getId() { + return mod.getModId(); + } + + @Override + public String getName() { + return mod.getName(); + } + + @Override + public String getVersion() { + return mod.getVersion(); + } + + @Override + public String getDescription() { + return mod.getMetadata().description; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardHandler.java new file mode 100644 index 0000000..29a970a --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardHandler.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import java.awt.Desktop; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** + * + * @author Stan + */ +public class MineTweakerCopyClipboardHandler implements IMessageHandler { + @Override + public IMessage onMessage(MineTweakerCopyClipboardPacket message, MessageContext ctx) { + if (Desktop.isDesktopSupported()) { + StringSelection stringSelection = new StringSelection(message.getData()); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } + + return null; + } +} \ No newline at end of file diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardPacket.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardPacket.java new file mode 100644 index 0000000..1a21bd4 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerCopyClipboardPacket.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import static com.google.common.base.Charsets.UTF_8; +import io.netty.buffer.ByteBuf; + +import java.nio.ByteBuffer; + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +/** + * + * @author Stan + */ +public class MineTweakerCopyClipboardPacket implements IMessage +{ + private String data; + + public MineTweakerCopyClipboardPacket() { + } + + public MineTweakerCopyClipboardPacket(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + @Override + public void fromBytes(ByteBuf buf) { + data = UTF_8.decode(ByteBuffer.wrap(buf.array())).toString().trim(); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeBytes(UTF_8.encode(data).array()); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsHandler.java new file mode 100644 index 0000000..6661f7d --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsHandler.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import minetweaker.MineTweakerAPI; +import minetweaker.MineTweakerImplementationAPI; +import minetweaker.mc18.client.MCClient; +import minetweaker.runtime.providers.ScriptProviderMemory; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** + * + * @author Stan + */ +public class MineTweakerLoadScriptsHandler implements IMessageHandler { + @Override + public IMessage onMessage(MineTweakerLoadScriptsPacket message, MessageContext ctx) { + if (MineTweakerAPI.server == null) { + MineTweakerAPI.client = new MCClient(); + + MineTweakerImplementationAPI.setScriptProvider(new ScriptProviderMemory(message.getData())); + MineTweakerImplementationAPI.reload(); + } + + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsPacket.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsPacket.java new file mode 100644 index 0000000..1c83e2f --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerLoadScriptsPacket.java @@ -0,0 +1,43 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +/** + * + * @author Stan + */ +public class MineTweakerLoadScriptsPacket implements IMessage { + private byte[] data; + + public MineTweakerLoadScriptsPacket() { + // used for deserialization + } + + public MineTweakerLoadScriptsPacket(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + + @Override + public void fromBytes(ByteBuf buf) { + int size = buf.readInt(); + data = new byte[size]; + buf.readBytes(data); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(data.length); + buf.writeBytes(data); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserHandler.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserHandler.java new file mode 100644 index 0000000..b92cd38 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserHandler.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; + +/** + * + * @author Stan + */ +public class MineTweakerOpenBrowserHandler implements IMessageHandler { + @Override + public IMessage onMessage(MineTweakerOpenBrowserPacket message, MessageContext ctx) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(new URI(message.getUrl())); + } catch (IOException e) { + } catch (URISyntaxException e) { + } + } else { + System.out.println("Desktop not supported"); + } + + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserPacket.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserPacket.java new file mode 100644 index 0000000..01ef93d --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/network/MineTweakerOpenBrowserPacket.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.network; + +import io.netty.buffer.ByteBuf; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + +/** + * + * @author Stan + */ +public class MineTweakerOpenBrowserPacket implements IMessage { + private static final Charset UTF8 = Charset.forName("utf-8"); + + private String url; + + public MineTweakerOpenBrowserPacket() { + + } + + public MineTweakerOpenBrowserPacket(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + @Override + public void fromBytes(ByteBuf buf) { + url = UTF8.decode(ByteBuffer.wrap(buf.array())).toString().trim(); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeBytes(UTF8.encode(url).array()); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDict.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDict.java new file mode 100644 index 0000000..85122b7 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDict.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.oredict; + +import static minetweaker.api.minecraft.MineTweakerMC.getOreDict; + +import java.util.ArrayList; +import java.util.List; + +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.oredict.IOreDict; +import minetweaker.api.oredict.IOreDictEntry; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCOreDict implements IOreDict { + @Override + public IOreDictEntry get(String name) { + return getOreDict(name); + } + + @Override + public List getEntries() { + List entries = new ArrayList(); + for (String key : OreDictionary.getOreNames()) { + if (!OreDictionary.getOres(key).isEmpty()) { + entries.add(MineTweakerMC.getOreDict(key)); + } + } + return entries; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDictEntry.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDictEntry.java new file mode 100644 index 0000000..251440c --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/oredict/MCOreDictEntry.java @@ -0,0 +1,395 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.oredict; + +import static minetweaker.api.minecraft.MineTweakerMC.getIItemStackWildcardSize; +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemCondition; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.IItemTransformer; +import minetweaker.api.item.IngredientOr; +import minetweaker.api.item.IngredientStack; +import minetweaker.api.liquid.ILiquidStack; +import minetweaker.api.oredict.IOreDictEntry; +import minetweaker.api.oredict.IngredientOreDict; +import minetweaker.api.player.IPlayer; +import minetweaker.mc18.util.MineTweakerHacks; +import minetweaker.util.ArrayUtil; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class MCOreDictEntry implements IOreDictEntry { + private static final List> OREDICT_CONTENTS = MineTweakerHacks.getOreIdStacks(); + private static final List> OREDICT_CONTENTS_UN = MineTweakerHacks.getOreIdStacksUn(); + + private final String id; + + public MCOreDictEntry(String id) { + this.id = id; + } + + + // #################################### + // ### IOreDictEntry implementation ### + // #################################### + + @Override + public String getName() { + return id; + } + + @Override + public boolean isEmpty() { + return OreDictionary.getOres(getName()).isEmpty(); + } + + @Override + public void add(IItemStack item) { + ItemStack stack = getItemStack(item); + if (stack != null) { + MineTweakerAPI.apply(new ActionAddItem(id, stack)); + } + } + + @Override + public void addAll(IOreDictEntry entry) { + if (entry instanceof MCOreDictEntry) { + MineTweakerAPI.apply(new ActionAddAll(id, ((MCOreDictEntry) entry).id)); + } else { + MineTweakerAPI.logError("not a valid entry"); + } + } + + @Override + public void remove(IItemStack item) { + ItemStack result = null; + for (ItemStack itemStack : OreDictionary.getOres(getName())) { + if (item.matches(getIItemStackWildcardSize(itemStack))) { + result = itemStack; + break; + } + } + + if (result != null) { + MineTweakerAPI.apply(new ActionRemoveItem(id, result)); + } + } + + @Override + public boolean contains(IItemStack item) { + for (ItemStack itemStack : OreDictionary.getOres(getName())) { + if (getIItemStackWildcardSize(itemStack).matches(item)) { + return true; + } + } + + return false; + } + + @Override + public void mirror(IOreDictEntry other) { + if (other instanceof MCOreDictEntry) { + MineTweakerAPI.apply(new ActionMirror(id, ((MCOreDictEntry) other).id)); + } else { + MineTweakerAPI.logError("not a valid oredict entry"); + } + } + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return 1; + } + + @Override + public List getItems() { + List result = new ArrayList(); + for (ItemStack item : OreDictionary.getOres(getName())) { + result.add(getIItemStackWildcardSize(item)); + } + return result; + } + + @Override + public List getLiquids() { + return Collections.emptyList(); + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientOreDict(this, null, ArrayUtil.EMPTY_CONDITIONS, new IItemTransformer[] { transformer }); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientOreDict(this, null, new IItemCondition[] { condition }, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientOreDict(this, mark, ArrayUtil.EMPTY_CONDITIONS, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient or(IIngredient ingredient) { + return new IngredientOr(this, ingredient); + } + + @Override + public boolean matches(IItemStack item) { + return contains(item); + } + + @Override + public boolean matches(ILiquidStack liquid) { + return false; + } + + @Override + public boolean contains(IIngredient ingredient) { + List items = ingredient.getItems(); + for (IItemStack item : items) { + if (!matches(item)) + return false; + } + + return true; + } + + @Override + public IItemStack applyTransform(IItemStack item, IPlayer byPlayer) { + return item; + } + + @Override + public boolean hasTransformers() { + return false; + } + + @Override + public Object getInternal() { + return id; + } + + // ############################# + // ### Object implementation ### + // ############################# + + @Override + public String toString() { + return ""; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof MCOreDictEntry)) + return false; + return ((MCOreDictEntry) other).id == id; + } + + // ###################### + // ### Action classes ### + // ###################### + + private static class ActionAddItem implements IUndoableAction { + private final String id; + private final ItemStack item; + + public ActionAddItem(String id, ItemStack item) { + this.id = id; + this.item = item; + } + + @Override + public void apply() { + OreDictionary.registerOre(id, item); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(id)).remove(item); + } + + @Override + public String describe() { + return "Adding " + item.getDisplayName() + " to ore dictionary entry " + id; + } + + @Override + public String describeUndo() { + return "Removing " + item.getDisplayName() + " from ore dictionary entry " + id; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class ActionMirror implements IUndoableAction { + private final String idTarget; + private final String idSource; + + private final ArrayList targetCopy; + private final ArrayList targetCopyUn; + + public ActionMirror(String idTarget, String idSource) { + this.idTarget = idTarget; + this.idSource = idSource; + + targetCopy = OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(idTarget)); + targetCopyUn = OREDICT_CONTENTS_UN.get(OREDICT_CONTENTS_UN.indexOf(idTarget)); + } + + @Override + public void apply() { + OREDICT_CONTENTS.set(OREDICT_CONTENTS.indexOf(idTarget), OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(idSource))); + OREDICT_CONTENTS_UN.set(OREDICT_CONTENTS_UN.indexOf(idTarget), OREDICT_CONTENTS_UN.get(OREDICT_CONTENTS_UN.indexOf(idSource))); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + OREDICT_CONTENTS.set(OREDICT_CONTENTS.indexOf(idTarget), targetCopy); + OREDICT_CONTENTS_UN.set(OREDICT_CONTENTS_UN.indexOf(idTarget), targetCopyUn); + } + + @Override + public String describe() { + return "Mirroring " + idSource + " to " + idTarget; + } + + @Override + public String describeUndo() { + return "Undoing mirror of " + idSource+ " to " + idTarget; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class ActionRemoveItem implements IUndoableAction { + private final String id; + private final ItemStack item; + + public ActionRemoveItem(String id, ItemStack item) { + this.id = id; + this.item = item; + } + + @Override + public void apply() { + OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(id)).remove(item); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(id)).add(item); + } + + @Override + public String describe() { + return "Removing " + item.getDisplayName() + " from ore dictionary entry " + id; + } + + @Override + public String describeUndo() { + return "Restoring " + item.getDisplayName() + " to ore dictionary entry " + id; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class ActionAddAll implements IUndoableAction { + private final String idTarget; + private final String idSource; + + public ActionAddAll(String idTarget, String idSource) { + this.idTarget = idTarget; + this.idSource = idSource; + } + + @Override + public void apply() { + for (ItemStack stack : OreDictionary.getOres(idSource)) { + OreDictionary.registerOre(idTarget, stack); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (ItemStack stack : OreDictionary.getOres(idSource)) { + OREDICT_CONTENTS.get(OREDICT_CONTENTS.indexOf(idTarget)).remove(stack); + } + } + + @Override + public String describe() { + return "Copying contents of ore dictionary entry " + idSource + " to " + idTarget; + } + + @Override + public String describeUndo() { + return "Removing contents of ore dictionary entry " + idSource + " from " + idTarget; + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/player/MCPlayer.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/player/MCPlayer.java new file mode 100644 index 0000000..6893ce6 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/player/MCPlayer.java @@ -0,0 +1,146 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.player; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.chat.IChatMessage; +import minetweaker.api.data.IData; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.player.IPlayer; +import minetweaker.mc18.MineTweakerMod; +import minetweaker.mc18.data.NBTConverter; +import minetweaker.mc18.network.MineTweakerCopyClipboardPacket; +import minetweaker.mc18.network.MineTweakerOpenBrowserPacket; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +/** + * + * @author Stan + */ +public class MCPlayer implements IPlayer { + private final EntityPlayer player; + + public MCPlayer(EntityPlayer player) { + this.player = player; + } + + public EntityPlayer getInternal() { + return player; + } + + @Override + public String getId() { + return null; // TODO: we should be having this for MC 1.7.10, right? + } + + @Override + public String getName() { + return player.getName(); + } + + @Override + public IData getData() { + return NBTConverter.from(player.getEntityData(), true); + } + + @Override + public void update(IData data) { + NBTConverter.updateMap(player.getEntityData(), data); + } + + @Override + public void sendChat(IChatMessage message) { + Object internal = message.getInternal(); + if (!(internal instanceof IChatComponent)) { + MineTweakerAPI.logError("not a valid chat message"); + return; + } + player.addChatMessage((IChatComponent) internal); + } + + @Override + public void sendChat(String message) { + player.addChatMessage(new ChatComponentText(message)); + } + + @Override + public int getHotbarSize() { + return 9; + } + + @Override + public IItemStack getHotbarStack(int i) { + return i < 0 || i >= 9 ? null : MineTweakerMC.getIItemStack(player.inventory.getStackInSlot(i)); + } + + @Override + public int getInventorySize() { + return player.inventory.getSizeInventory(); + } + + @Override + public IItemStack getInventoryStack(int i) { + return MineTweakerMC.getIItemStack(player.inventory.getStackInSlot(i)); + } + + @Override + public IItemStack getCurrentItem() { + return MineTweakerMC.getIItemStack(player.getCurrentEquippedItem()); + } + + @Override + public boolean isCreative() { + return player.capabilities.isCreativeMode; + } + + @Override + public boolean isAdventure() { + return !player.capabilities.allowEdit; + } + + @Override + public void openBrowser(String url) { + if (player instanceof EntityPlayerMP) { + MineTweakerMod.NETWORK.sendTo( + new MineTweakerOpenBrowserPacket(url), + (EntityPlayerMP) player); + } + } + + @Override + public void copyToClipboard(String value) { + if (player instanceof EntityPlayerMP) { + MineTweakerMod.NETWORK.sendTo( + new MineTweakerCopyClipboardPacket(value), + (EntityPlayerMP) player); + } + } + + @Override + public boolean equals(Object other) { + if (other.getClass() != this.getClass()) + return false; + + return ((MCPlayer) other).player == player; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 23 * hash + (this.player != null ? this.player.hashCode() : 0); + return hash; + } + + @Override + public void give(IItemStack stack) { + player.inventory.addItemStackToInventory(MineTweakerMC.getItemStack(stack).copy()); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCCraftingInventory.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCCraftingInventory.java new file mode 100644 index 0000000..f384262 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCCraftingInventory.java @@ -0,0 +1,214 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getIItemStack; +import static minetweaker.api.minecraft.MineTweakerMC.getIPlayer; +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; + +import java.util.List; + +import minetweaker.api.item.IItemStack; +import minetweaker.api.player.IPlayer; +import minetweaker.api.recipes.ICraftingInventory; +import minetweaker.mc18.player.MCPlayer; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.inventory.SlotCrafting; +import net.minecraft.item.ItemStack; + +/** + * + * @author Stan + */ +public class MCCraftingInventory implements ICraftingInventory { + private static final ThreadLocal cache = new ThreadLocal(); + private static final ThreadLocal cache2 = new ThreadLocal(); + + public static MCCraftingInventory get(InventoryCrafting inventory) { + if (cache.get() == null || cache.get().inventory != inventory) { + MCCraftingInventory result = new MCCraftingInventory(inventory); + cache.set(result); + return result; + } else { + MCCraftingInventory result = cache.get(); + result.update(); + return result; + } + } + + public static MCCraftingInventory get(IInventory inventory, EntityPlayer player) { + if (cache2.get() == null || cache2.get().inventory != inventory || cache2.get().player != player) { + MCCraftingInventory result = new MCCraftingInventory(inventory, player); + cache2.set(result); + return result; + } else { + MCCraftingInventory result = cache2.get(); + result.update(); + return result; + } + } + + private int width; + private int height; + private final IInventory inventory; + private IItemStack[] stacks; + private ItemStack[] original; + private int stackCount; + private final IPlayer player; + private final EntityPlayer playerOrig; + + private MCCraftingInventory(InventoryCrafting inventory) { + this.inventory = inventory; + width = height = (int) Math.sqrt(inventory.getSizeInventory()); + stacks = new IItemStack[width * height]; + original = new ItemStack[stacks.length]; + stackCount = 0; + update(); + + Container container = MineTweakerHacks.getCraftingContainer(inventory); + if (container != null) { + List slots = container.inventorySlots; + if (!slots.isEmpty() && slots.get(0) instanceof SlotCrafting) { + SlotCrafting slotCrafting = (SlotCrafting) slots.get(0); + playerOrig = MineTweakerHacks.getCraftingSlotPlayer(slotCrafting); + player = getIPlayer(playerOrig); + } else { + playerOrig = null; + player = null; + } + } else { + playerOrig = null; + player = null; + } + } + + public MCCraftingInventory(IInventory inventory, EntityPlayer player) { + this.inventory = inventory; + width = height = (int) Math.sqrt(inventory.getSizeInventory()); + stacks = new IItemStack[width * height]; + original = new ItemStack[stacks.length]; + stackCount = 0; + update(); + + playerOrig = player; + this.player = player == null ? null : new MCPlayer(player); + } + + private void update() { + if (inventory.getSizeInventory() != original.length) { + width = height = (int) Math.sqrt(inventory.getSizeInventory()); + stacks = new IItemStack[inventory.getSizeInventory()]; + original = new ItemStack[stacks.length]; + stackCount = 0; + } + + for (int i = 0; i < inventory.getSizeInventory(); i++) { + if (changed(i)) { + // System.out.println("Slot " + i + " changed"); + original[i] = inventory.getStackInSlot(i); + if (inventory.getStackInSlot(i) != null) { + if (stacks[i] == null) + stackCount++; + stacks[i] = getIItemStack(original[i]); + } else { + if (stacks[i] != null) + stackCount--; + stacks[i] = null; + } + } + } + // System.out.println("Num stack count: " + stackCount); + } + + @Override + public IPlayer getPlayer() { + return player; + } + + @Override + public int getSize() { + return width * height; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getStackCount() { + return stackCount; + } + + @Override + public IItemStack getStack(int i) { + return stacks[i]; + } + + @Override + public IItemStack getStack(int x, int y) { + return stacks[y * width + x]; + } + + @Override + public void setStack(int x, int y, IItemStack stack) { + // System.out.println("SetStack(" + x + ", " + y + ") " + stack); + + int ix = y * width + x; + if (stack != stacks[ix]) { + if (stack == null) { + stackCount--; + inventory.setInventorySlotContents(ix, null); + } else { + inventory.setInventorySlotContents(ix, getItemStack(stack)); + + if (stacks[ix] == null) { + stackCount++; + } + } + stacks[ix] = stack; + } + } + + @Override + public void setStack(int i, IItemStack stack) { + // System.out.println("SetStack(" + i + ") " + stack); + + if (stack != stacks[i]) { + if (stack == null) { + stackCount--; + inventory.setInventorySlotContents(i, null); + } else { + inventory.setInventorySlotContents(i, getItemStack(stack)); + + if (stacks[i] == null) { + stackCount++; + } + } + stacks[i] = stack; + } + } + + private boolean changed(int i) { + if (original[i] != inventory.getStackInSlot(i)) + return true; + if (original[i] != null && stacks[i].getAmount() != original[i].stackSize) + return true; + + return false; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCRecipeManager.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCRecipeManager.java new file mode 100644 index 0000000..202a608 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/MCRecipeManager.java @@ -0,0 +1,422 @@ +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getIItemStack; +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import static minetweaker.api.minecraft.MineTweakerMC.getOreDict; + +import java.util.ArrayList; +import java.util.List; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.player.IPlayer; +import minetweaker.api.recipes.ICraftingInventory; +import minetweaker.api.recipes.ICraftingRecipe; +import minetweaker.api.recipes.IRecipeFunction; +import minetweaker.api.recipes.IRecipeManager; +import minetweaker.api.recipes.ShapedRecipe; +import minetweaker.api.recipes.ShapelessRecipe; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +/** + * + * @author Stan + */ +public class MCRecipeManager implements IRecipeManager { + private final List recipes; + private final List transformerRecipes; + + public MCRecipeManager() { + recipes = (List) CraftingManager.getInstance().getRecipeList(); + transformerRecipes = new ArrayList(); + } + + public boolean hasTransformerRecipes() { + return transformerRecipes.size() > 0; + } + + public void applyTransformations(ICraftingInventory inventory, IPlayer byPlayer) { + for (ICraftingRecipe recipe : transformerRecipes) { + if (recipe.matches(inventory)) { + recipe.applyTransformers(inventory, byPlayer); + return; + } + } + } + + @Override + public List getRecipesFor(IIngredient ingredient) { + List results = new ArrayList(); + + for (IRecipe recipe : recipes) { + if (ingredient.matches(MineTweakerMC.getIItemStack(recipe.getRecipeOutput()))) { + ICraftingRecipe converted = RecipeConverter.toCraftingRecipe(recipe); + results.add(converted); + } + } + + return results; + } + + @Override + public List getAll() { + List results = new ArrayList(); + + for (IRecipe recipe : recipes) { + ICraftingRecipe converted = RecipeConverter.toCraftingRecipe(recipe); + results.add(converted); + } + + return results; + } + + @Override + public int remove(IIngredient output) { + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + // certain special recipes have no predefined output. ignore those + // since these cannot be removed with MineTweaker scripts + if (recipe.getRecipeOutput() != null) { + if (output.matches(getIItemStack(recipe.getRecipeOutput()))) { + toRemove.add(recipe); + removeIndex.add(i); + } + } + } + + MineTweakerAPI.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public void addShaped(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function) { + addShaped(output, ingredients, function, false); + } + + @Override + public void addShapedMirrored(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function) { + addShaped(output, ingredients, function, true); + } + + @Override + public void addShapeless(IItemStack output, IIngredient[] ingredients, IRecipeFunction function) { + ShapelessRecipe recipe = new ShapelessRecipe(output, ingredients, function); + IRecipe irecipe = RecipeConverter.convert(recipe); + MineTweakerAPI.apply(new ActionAddRecipe(irecipe, recipe)); + } + + @Override + public int removeShaped(IIngredient output, IIngredient[][] ingredients) { + int ingredientsWidth = 0; + int ingredientsHeight = 0; + + if (ingredients != null) { + ingredientsHeight = ingredients.length; + + for (int i = 0; i < ingredients.length; i++) { + ingredientsWidth = Math.max(ingredientsWidth, ingredients[i].length); + } + } + + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + outer: for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + if (recipe.getRecipeOutput() == null || !output.matches(getIItemStack(recipe.getRecipeOutput()))) { + continue; + } + + if (ingredients != null) { + if (recipe instanceof ShapedRecipes) { + ShapedRecipes srecipe = (ShapedRecipes) recipe; + if (ingredientsWidth != srecipe.recipeWidth || ingredientsHeight != srecipe.recipeHeight) { + continue; + } + + for (int j = 0; j < ingredientsHeight; j++) { + IIngredient[] row = ingredients[j]; + for (int k = 0; k < ingredientsWidth; k++) { + IIngredient ingredient = k > row.length ? null : row[k]; + ItemStack recipeIngredient = srecipe.recipeItems[j * srecipe.recipeWidth + k]; + + if (!matches(recipeIngredient, ingredient)) { + continue outer; + } + } + } + } else if (recipe instanceof ShapedOreRecipe) { + ShapedOreRecipe srecipe = (ShapedOreRecipe) recipe; + int recipeWidth = MineTweakerHacks.getShapedOreRecipeWidth(srecipe); + int recipeHeight = srecipe.getRecipeSize() / recipeWidth; + if (ingredientsWidth != recipeWidth || ingredientsHeight != recipeHeight) { + continue; + } + + for (int j = 0; j < ingredientsHeight; j++) { + IIngredient[] row = ingredients[j]; + for (int k = 0; k < ingredientsWidth; k++) { + IIngredient ingredient = k > row.length ? null : row[k]; + Object input = srecipe.getInput()[j * recipeWidth + k]; + if (!matches(input, ingredient)) { + continue outer; + } + } + } + } + } else { + if (recipe instanceof ShapedRecipes) { + + } else if (recipe instanceof ShapedOreRecipe) { + + } else { + continue; + } + } + + toRemove.add(recipe); + removeIndex.add(i); + } + + MineTweakerAPI.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public int removeShapeless(IIngredient output, IIngredient[] ingredients, boolean wildcard) { + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + outer: for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + if (recipe.getRecipeOutput() == null || !output.matches(getIItemStack(recipe.getRecipeOutput()))) { + continue; + } + + if (ingredients != null) { + if (recipe instanceof ShapelessRecipes) { + ShapelessRecipes srecipe = (ShapelessRecipes) recipe; + + if (ingredients.length > srecipe.getRecipeSize()) { + continue; + } else if (!wildcard && ingredients.length < srecipe.getRecipeSize()) { + continue; + } + + checkIngredient: for (int j = 0; j < ingredients.length; j++) { + for (int k = 0; k < srecipe.getRecipeSize(); k++) { + if (matches(srecipe.recipeItems.get(k), ingredients[j])) { + continue checkIngredient; + } + } + + continue outer; + } + } else if (recipe instanceof ShapelessOreRecipe) { + ShapelessOreRecipe srecipe = (ShapelessOreRecipe) recipe; + ArrayList inputs = srecipe.getInput(); + + if (inputs.size() < ingredients.length) { + continue; + } + if (!wildcard && inputs.size() > ingredients.length) { + continue; + } + + checkIngredient: for (int j = 0; j < ingredients.length; j++) { + for (int k = 0; k < srecipe.getRecipeSize(); k++) { + if (matches(inputs.get(k), ingredients[j])) { + continue checkIngredient; + } + } + + continue outer; + } + } + } else { + if (recipe instanceof ShapelessRecipes) { + + } else if (recipe instanceof ShapelessOreRecipe) { + + } else { + continue; + } + } + + toRemove.add(recipe); + removeIndex.add(i); + } + + MineTweakerAPI.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public IItemStack craft(IItemStack[][] contents) { + Container container = new ContainerVirtual(); + + int width = 0; + int height = contents.length; + for (IItemStack[] row : contents) { + width = Math.max(width, row.length); + } + + ItemStack[] iContents = new ItemStack[width * height]; + for (int i = 0; i < height; i++) { + for (int j = 0; j < contents[i].length; j++) { + if (contents[i][j] != null) { + iContents[i * width + j] = getItemStack(contents[i][j]); + } + } + } + + InventoryCrafting inventory = new InventoryCrafting(container, width, height); + for (int i = 0; i < iContents.length; i++) { + inventory.setInventorySlotContents(i, iContents[i]); + } + ItemStack result = CraftingManager.getInstance().findMatchingRecipe(inventory, null); + if (result == null) { + return null; + } else { + return getIItemStack(result); + } + } + + private class ContainerVirtual extends Container { + @Override + public boolean canInteractWith(EntityPlayer var1) { + return false; + } + } + + private class ActionRemoveRecipes implements IUndoableAction { + private final List removingIndices; + private final List removingRecipes; + + public ActionRemoveRecipes(List recipes, List indices) { + this.removingIndices = indices; + this.removingRecipes = recipes; + } + + @Override + public void apply() { + for (int i = removingIndices.size() - 1; i >= 0; i--) { + recipes.remove((int) removingIndices.get(i)); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (int i = 0; i < removingIndices.size(); i++) { + int index = Math.min(recipes.size(), removingIndices.get(i)); + recipes.add(index, removingRecipes.get(i)); + } + } + + @Override + public String describe() { + return "Removing " + removingIndices.size() + " recipes"; + } + + @Override + public String describeUndo() { + return "Restoring " + removingIndices.size() + " recipes"; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class ActionAddRecipe implements IUndoableAction { + private final IRecipe recipe; + private final ICraftingRecipe craftingRecipe; + + public ActionAddRecipe(IRecipe recipe, ICraftingRecipe craftingRecipe) { + this.recipe = recipe; + this.craftingRecipe = craftingRecipe; + } + + @Override + public void apply() { + recipes.add(recipe); + if (craftingRecipe.hasTransformers()) { + transformerRecipes.add(craftingRecipe); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + recipes.remove(recipe); + if (craftingRecipe.hasTransformers()) { + transformerRecipes.remove(craftingRecipe); + } + } + + @Override + public String describe() { + return "Adding recipe for " + recipe.getRecipeOutput().getDisplayName(); + } + + @Override + public String describeUndo() { + return "Removing recipe for " + recipe.getRecipeOutput().getDisplayName(); + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private void addShaped(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function, boolean mirrored) { + ShapedRecipe recipe = new ShapedRecipe(output, ingredients, function, mirrored); + IRecipe irecipe = RecipeConverter.convert(recipe); + MineTweakerAPI.apply(new ActionAddRecipe(irecipe, recipe)); + } + + private static boolean matches(Object input, IIngredient ingredient) { + if ((input == null) != (ingredient == null)) { + return false; + } else if (ingredient != null) { + if (input instanceof ItemStack) { + if (!ingredient.matches(getIItemStack((ItemStack) input))) { + return false; + } + } else if (input instanceof String) { + if (!ingredient.contains(getOreDict((String) input))) { + return false; + } + } + } + + return true; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/RecipeConverter.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/RecipeConverter.java new file mode 100644 index 0000000..dde1e69 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/RecipeConverter.java @@ -0,0 +1,183 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; + +import java.util.ArrayList; +import java.util.Arrays; + +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.recipes.ICraftingRecipe; +import minetweaker.api.recipes.ShapedRecipe; +import minetweaker.api.recipes.ShapelessRecipe; +import minetweaker.api.recipes.UnknownRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +/** + * + * @author Stan + */ +public class RecipeConverter { + private RecipeConverter() { + } + + public static final int TYPE_ADVANCED = 0; + public static final int TYPE_ORE = 1; + public static final int TYPE_BASIC = 2; + + private static int getIngredientType(IIngredient pattern) { + Object internal = pattern.getInternal(); + if (internal == null) { + return TYPE_ADVANCED; + } else if (internal instanceof ItemStack) { + return TYPE_BASIC; + } else { + return TYPE_ORE; + } + } + + private static int getRecipeType(IIngredient[] ingredients) { + int type = TYPE_BASIC; + for (IIngredient ingredient : ingredients) { + type = Math.min(type, getIngredientType(ingredient)); + } + return type; + } + + public static IRecipe convert(ShapelessRecipe recipe) { + IIngredient[] ingredients = recipe.getIngredients(); + int type = getRecipeType(ingredients); + + if (type == TYPE_BASIC) { + ItemStack[] items = new ItemStack[ingredients.length]; + for (int i = 0; i < ingredients.length; i++) { + items[i] = getItemStack(ingredients[i]); + } + return new ShapelessRecipeBasic(items, recipe); + } else if (type == TYPE_ORE) { + Object[] items = new Object[ingredients.length]; + for (int i = 0; i < ingredients.length; i++) { + items[i] = ingredients[i].getInternal(); + } + return new ShapelessRecipeOre(items, recipe); + } else { + return new ShapelessRecipeAdvanced(recipe); + } + } + + public static IRecipe convert(ShapedRecipe recipe) { + IIngredient[] ingredients = recipe.getIngredients(); + byte[] posx = recipe.getIngredientsX(); + byte[] posy = recipe.getIngredientsY(); + + // determine recipe type + int type = getRecipeType(ingredients); + + // construct recipe + if (type == TYPE_BASIC) { + ItemStack[] basicIngredients = new ItemStack[recipe.getHeight() * recipe.getWidth()]; + for (int i = 0; i < ingredients.length; i++) { + basicIngredients[posx[i] + posy[i] * recipe.getWidth()] = getItemStack(ingredients[i]); + } + + return new ShapedRecipeBasic(basicIngredients, recipe); + } else if (type == TYPE_ORE) { + Object[] converted = new Object[recipe.getHeight() * recipe.getWidth()]; + for (int i = 0; i < ingredients.length; i++) { + converted[posx[i] + posy[i] * recipe.getWidth()] = ingredients[i].getInternal(); + } + + // arguments contents: + // 1) recipe patterns + // 2) characters + ingredients + + int counter = 0; + String[] parts = new String[recipe.getHeight()]; + ArrayList rarguments = new ArrayList(); + for (int i = 0; i < recipe.getHeight(); i++) { + char[] pattern = new char[recipe.getWidth()]; + for (int j = 0; j < recipe.getWidth(); j++) { + int off = i * recipe.getWidth() + j; + if (converted[off] == null) { + pattern[j] = ' '; + } else { + pattern[j] = (char) ('A' + counter); + rarguments.add(pattern[j]); + rarguments.add(converted[off]); + counter++; + } + } + parts[i] = new String(pattern); + } + + rarguments.addAll(0, Arrays.asList(parts)); + + return new ShapedRecipeOre(rarguments.toArray(), recipe); + } else { + return new ShapedRecipeAdvanced(recipe); + } + } + + public static ICraftingRecipe toCraftingRecipe(IRecipe recipe) { + IItemStack output = MineTweakerMC.getIItemStack(recipe.getRecipeOutput()); + + if (recipe instanceof ShapelessRecipes) { + ShapelessRecipes shapeless = (ShapelessRecipes) recipe; + + IIngredient[] ingredients = new IIngredient[shapeless.recipeItems.size()]; + for (int i = 0; i < ingredients.length; i++) { + ingredients[i] = MineTweakerMC.getIIngredient(shapeless.recipeItems.get(i)); + } + + return new ShapelessRecipe(output, ingredients, null); + } else if (recipe instanceof ShapedRecipes) { + ShapedRecipes shaped = (ShapedRecipes) recipe; + + IIngredient[][] ingredients = new IIngredient[shaped.recipeHeight][shaped.recipeWidth]; + for (int i = 0; i < shaped.recipeHeight; i++) { + for (int j = 0; j < shaped.recipeWidth; j++) { + ingredients[i][j] = MineTweakerMC.getIItemStack(shaped.recipeItems[i * shaped.recipeWidth + j]); + } + } + + return new ShapedRecipe(output, ingredients, null, false); + } else if (recipe instanceof ShapedOreRecipe) { + ShapedOreRecipe shaped = (ShapedOreRecipe) recipe; + + int width = (int) Math.sqrt(recipe.getRecipeSize()); + int height = recipe.getRecipeSize() / width; + + IIngredient[][] recipeIngredients = new IIngredient[height][width]; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + recipeIngredients[i][j] = MineTweakerMC.getIIngredient(shaped.getInput()[i * width + j]); + } + } + + return new ShapedRecipe(output, recipeIngredients, null, false); + } else if (recipe instanceof ShapelessOreRecipe) { + ShapelessOreRecipe shapeless = (ShapelessOreRecipe) recipe; + + IIngredient[] ingredients = new IIngredient[shapeless.getRecipeSize()]; + for (int i = 0; i < ingredients.length; i++) { + ingredients[i] = MineTweakerMC.getIIngredient(shapeless.getInput().get(i)); + } + + return new ShapelessRecipe(output, ingredients, null); + } else { + return new UnknownRecipe(output); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeAdvanced.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeAdvanced.java new file mode 100644 index 0000000..f9d83d8 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeAdvanced.java @@ -0,0 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import minetweaker.api.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.world.World; + +/** + * TODO: NEI integration for this kind of recipes. + * + * @author Stan + */ +public class ShapedRecipeAdvanced implements IRecipe { + private final ShapedRecipe recipe; + + public ShapedRecipeAdvanced(ShapedRecipe recipe) { + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return getItemStack(recipe.getCraftingResult(MCCraftingInventory.get(inventory))).copy(); + } + + @Override + public int getRecipeSize() { + return recipe.getWidth() * recipe.getHeight(); + } + + @Override + public ItemStack getRecipeOutput() { + return getItemStack(recipe.getOutput()); + } + + @Override + public ItemStack[] getRemainingItems(InventoryCrafting p_179532_1_) { + return new ItemStack[]{}; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeBasic.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeBasic.java new file mode 100644 index 0000000..29564d2 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeBasic.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import minetweaker.api.item.IItemStack; +import minetweaker.api.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapedRecipeBasic extends ShapedRecipes { + private final ShapedRecipe recipe; + + public ShapedRecipeBasic(ItemStack[] basicInputs, ShapedRecipe recipe) { + super(recipe.getWidth(), recipe.getHeight(), basicInputs, getItemStack(recipe.getOutput())); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + IItemStack result = recipe.getCraftingResult(MCCraftingInventory.get(inventory)); + if (result == null) { + return null; + } else { + return getItemStack(result).copy(); + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeOre.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeOre.java new file mode 100644 index 0000000..ec5aaf3 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapedRecipeOre.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import minetweaker.api.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapedOreRecipe; + +/** + * + * @author Stan + */ +public class ShapedRecipeOre extends ShapedOreRecipe { + private final ShapedRecipe recipe; + + public ShapedRecipeOre(Object[] contents, ShapedRecipe recipe) { + super(getItemStack(recipe.getOutput()), contents); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return getItemStack(recipe.getCraftingResult(MCCraftingInventory.get(inventory))).copy(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeAdvanced.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeAdvanced.java new file mode 100644 index 0000000..592daf7 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeAdvanced.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import minetweaker.api.recipes.ShapelessRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapelessRecipeAdvanced implements IRecipe { + private final ShapelessRecipe recipe; + + public ShapelessRecipeAdvanced(ShapelessRecipe recipe) { + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return getItemStack(recipe.getCraftingResult(MCCraftingInventory.get(inventory))).copy(); + } + + @Override + public int getRecipeSize() { + return recipe.getSize(); + } + + @Override + public ItemStack getRecipeOutput() { + return getItemStack(recipe.getOutput()); + } + + @Override + public ItemStack[] getRemainingItems(InventoryCrafting p_179532_1_) { + return new ItemStack[] {}; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeBasic.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeBasic.java new file mode 100644 index 0000000..ca87458 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeBasic.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; + +import java.util.Arrays; + +import minetweaker.api.recipes.ShapelessRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapelessRecipeBasic extends ShapelessRecipes { + private final ShapelessRecipe recipe; + + public ShapelessRecipeBasic(ItemStack[] ingredients, ShapelessRecipe recipe) { + super(getItemStack(recipe.getOutput()), Arrays.asList(ingredients)); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return getItemStack(recipe.getCraftingResult(MCCraftingInventory.get(inventory))).copy(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeOre.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeOre.java new file mode 100644 index 0000000..bf717b0 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/recipes/ShapelessRecipeOre.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.recipes; + +import static minetweaker.api.minecraft.MineTweakerMC.getItemStack; +import minetweaker.api.recipes.ShapelessRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +/** + * + * @author Stan + */ +public class ShapelessRecipeOre extends ShapelessOreRecipe { + private final ShapelessRecipe recipe; + + public ShapelessRecipeOre(Object[] ingredients, ShapelessRecipe recipe) { + super(getItemStack(recipe.getOutput()), ingredients); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(MCCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return getItemStack(recipe.getCraftingResult(MCCraftingInventory.get(inventory))).copy(); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/MCServer.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/MCServer.java new file mode 100644 index 0000000..f1eb471 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/MCServer.java @@ -0,0 +1,237 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.server; + +import java.util.Arrays; +import java.util.List; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.player.IPlayer; +import minetweaker.api.server.AbstractServer; +import minetweaker.api.server.ICommandFunction; +import minetweaker.api.server.ICommandTabCompletion; +import minetweaker.api.server.ICommandValidator; +import net.minecraft.command.CommandException; +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommand; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.management.UserListOps; +import net.minecraft.util.BlockPos; + +/** + * + * @author Stan + */ +public class MCServer extends AbstractServer { + private final MinecraftServer server; + + public MCServer(MinecraftServer server) { + this.server = server; + } + + @Override + public void addCommand(String name, String usage, String[] aliases, ICommandFunction function, ICommandValidator validator, ICommandTabCompletion completion) { + ICommand command = new MCCommand(name, usage, aliases, function, validator, completion); + MineTweakerAPI.apply(new AddCommandAction(command)); + } + + @Override + public void removeCommand(String name) { + ICommand command = (ICommand) ((CommandHandler) server.getCommandManager()).getCommands().get(name); + if (command == null) { + MineTweakerAPI.logWarning("No such command: " + name); + } else { + MineTweakerAPI.apply(new RemoveCommandAction(command)); + } + } + + @Override + public boolean isOp(IPlayer player) { + if (player == ServerPlayer.INSTANCE) + return true; + + UserListOps ops = MinecraftServer.getServer().getConfigurationManager().getOppedPlayers(); + if (server.isDedicatedServer()) { + return ops.hasEntries() || ops.getGameProfileFromName(player.getName()) != null; + } else { + return true; + } + } + + private static IPlayer getPlayer(ICommandSender commandSender) { + if (commandSender instanceof EntityPlayer) { + return MineTweakerMC.getIPlayer((EntityPlayer) commandSender); + } else if (commandSender instanceof DedicatedServer) { + return ServerPlayer.INSTANCE; + } else { + System.out.println("Unsupported command sender: " + commandSender); + System.out.println("player name: " + commandSender.getName()); + return null; + } + } + + private static void removeCommand(ICommand command) { + CommandHandler ch = (CommandHandler) MinecraftServer.getServer().getCommandManager(); + ch.getCommands().remove(command.getName()); + + if (command.getAliases() != null) { + for (String alias : (List) command.getAliases()) { + ch.getCommands().remove(alias); + } + } + } + + private class MCCommand implements ICommand { + private final String name; + private final String usage; + private final List aliases; + private final ICommandFunction function; + private final ICommandValidator validator; + private final ICommandTabCompletion completion; + + public MCCommand(String name, String usage, String[] aliases, ICommandFunction function, ICommandValidator validator, ICommandTabCompletion completion) { + this.name = name; + this.usage = usage; + this.aliases = Arrays.asList(aliases); + this.function = function; + this.validator = validator; + this.completion = completion; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCommandUsage(ICommandSender var1) { + return usage; + } + + @Override + public List getAliases() { + return aliases; + } + + @Override + public boolean canCommandSenderUse(ICommandSender var1) { + if (validator == null) { + return true; + } else { + return validator.canExecute(getPlayer(var1)); + } + } + + + @Override + public boolean isUsernameIndex(String[] var1, int var2) { + return false; + } + + @Override + public int compareTo(Object o) { + return 0; + } + + @Override + public void execute(ICommandSender sender, String[] args) throws CommandException { + function.execute(args, getPlayer(sender)); + } + + @Override + public List addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) { + if (completion != null) { + return Arrays.asList(completion.getTabCompletionOptions(args, getPlayer(sender))); + } else { + return null; + } + } + } + + private class AddCommandAction implements IUndoableAction { + private final ICommand command; + + public AddCommandAction(ICommand command) { + this.command = command; + } + + @Override + public void apply() { + CommandHandler ch = (CommandHandler) MinecraftServer.getServer().getCommandManager(); + ch.registerCommand(command); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + removeCommand(command); + } + + @Override + public String describe() { + return "Adding command " + command.getName(); + } + + @Override + public String describeUndo() { + return "Removing command " + command.getName(); + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private class RemoveCommandAction implements IUndoableAction { + private final ICommand command; + + public RemoveCommandAction(ICommand command) { + this.command = command; + } + + @Override + public void apply() { + removeCommand(command); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + CommandHandler ch = (CommandHandler) MinecraftServer.getServer().getCommandManager(); + ch.registerCommand(command); + } + + @Override + public String describe() { + return "Adding command " + command.getName(); + } + + @Override + public String describeUndo() { + return "Removing command " + command.getName(); + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/ServerPlayer.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/ServerPlayer.java new file mode 100644 index 0000000..772f8c6 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/server/ServerPlayer.java @@ -0,0 +1,127 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.server; + +import java.awt.Desktop; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.io.IOException; +import java.net.URI; +import java.util.logging.Level; +import java.util.logging.Logger; + +import minetweaker.api.chat.IChatMessage; +import minetweaker.api.data.IData; +import minetweaker.api.item.IItemStack; +import minetweaker.api.player.IPlayer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; + +/** + * + * @author Stan + */ +public class ServerPlayer implements IPlayer { + public static final ServerPlayer INSTANCE = new ServerPlayer(); + + private ServerPlayer() { + + } + + @Override + public String getId() { + return "server"; + } + + @Override + public String getName() { + return "Server"; + } + + @Override + public IData getData() { + // TODO: implement + return null; + } + + @Override + public void update(IData data) { + // TODO: implement + } + + @Override + public void sendChat(IChatMessage message) { + DedicatedServer.getServer().addChatMessage((IChatComponent) message.getInternal()); + } + + @Override + public void sendChat(String message) { + DedicatedServer.getServer().addChatMessage(new ChatComponentText(message)); + } + + @Override + public int getHotbarSize() { + return 0; + } + + @Override + public IItemStack getHotbarStack(int i) { + return null; + } + + @Override + public int getInventorySize() { + return 0; + } + + @Override + public IItemStack getInventoryStack(int i) { + return null; + } + + @Override + public IItemStack getCurrentItem() { + return null; + } + + @Override + public boolean isCreative() { + return true; + } + + @Override + public boolean isAdventure() { + return false; + } + + @Override + public void openBrowser(String url) { + if (Desktop.isDesktopSupported()) { + try { + Desktop.getDesktop().browse(URI.create(url)); + } catch (IOException ex) { + Logger.getLogger(ServerPlayer.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + @Override + public void copyToClipboard(String value) { + if (Desktop.isDesktopSupported()) { + StringSelection stringSelection = new StringSelection(value); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + } + } + + @Override + public void give(IItemStack stack) { + + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerHacks.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerHacks.java new file mode 100644 index 0000000..ff06ea5 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerHacks.java @@ -0,0 +1,291 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.util; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.WeightedItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.SlotCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.StatCollector; +import net.minecraft.util.StringTranslate; +import net.minecraft.util.WeightedRandom; +import net.minecraftforge.common.ChestGenHooks; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.fml.common.registry.EntityRegistry; +import net.minecraftforge.oredict.OreDictionary; +import net.minecraftforge.oredict.ShapedOreRecipe; + +import com.google.common.collect.BiMap; + +/** + * Common class for all runtime hacks (stuff requiring reflection). It is not + * unexpected to have it break with new minecraft versions and may need regular + * adjustment - as such, those have been collected here. + * + * @author Stan Hebben + */ +public class MineTweakerHacks { + private static final Field NBTTAGLIST_TAGLIST; + private static final Field OREDICTIONARY_IDTOSTACK; + private static final Field OREDICTIONARY_IDTOSTACKUN; + private static final Field MINECRAFTSERVER_ANVILFILE; + private static final Field SHAPEDORERECIPE_WIDTH; + private static final Field INVENTORYCRAFTING_EVENTHANDLER; + private static final Field SLOTCRAFTING_PLAYER; + + private static final Field ENTITYREGISTRY_CLASSREGISTRATIONS; + private static final Field SEEDENTRY_SEED; + private static final Constructor SEEDENTRY_CONSTRUCTOR; + + static { + NBTTAGLIST_TAGLIST = getField(NBTTagList.class, MineTweakerObfuscation.NBTTAGLIST_TAGLIST); + OREDICTIONARY_IDTOSTACK = getField(OreDictionary.class, MineTweakerObfuscation.OREDICTIONARY_IDTOSTACK); + OREDICTIONARY_IDTOSTACKUN = getField(OreDictionary.class, MineTweakerObfuscation.OREDICTIONARY_IDTOSTACKUN); + MINECRAFTSERVER_ANVILFILE = getField(MinecraftServer.class, MineTweakerObfuscation.MINECRAFTSERVER_ANVILFILE); + SHAPEDORERECIPE_WIDTH = getField(ShapedOreRecipe.class, new String[] { "width" }); + INVENTORYCRAFTING_EVENTHANDLER = getField(InventoryCrafting.class, MineTweakerObfuscation.INVENTORYCRAFTING_EVENTHANDLER); + SLOTCRAFTING_PLAYER = getField(SlotCrafting.class, MineTweakerObfuscation.SLOTCRAFTING_PLAYER); + + ENTITYREGISTRY_CLASSREGISTRATIONS = getField(EntityRegistry.class, new String[] { "entityClassRegistrations" }); + + Class forgeSeedEntry = null; + try { + forgeSeedEntry = (Class) Class.forName("net.minecraftforge.common.ForgeHooks$SeedEntry"); + } catch (ClassNotFoundException ex) { + } + + SEEDENTRY_SEED = getField(forgeSeedEntry, "seed"); + + Constructor seedEntryConstructor = null; + + try { + seedEntryConstructor = forgeSeedEntry.getConstructor(ItemStack.class, int.class); + seedEntryConstructor.setAccessible(true); + } catch (NoSuchMethodException ex) { + Logger.getLogger(MineTweakerHacks.class.getName()).log(Level.SEVERE, null, ex); + } catch (SecurityException ex) { + Logger.getLogger(MineTweakerHacks.class.getName()).log(Level.SEVERE, null, ex); + } + + SEEDENTRY_CONSTRUCTOR = seedEntryConstructor; + } + + private MineTweakerHacks() { + } + + public static BiMap, EntityRegistry.EntityRegistration> getEntityClassRegistrations() { + try { + return (BiMap, EntityRegistry.EntityRegistration>) ENTITYREGISTRY_CLASSREGISTRATIONS.get(EntityRegistry.instance()); + } catch (IllegalArgumentException ex) { + return null; + } catch (IllegalAccessException ex) { + return null; + } + } + + public static List getTagList(NBTTagList list) { + if (NBTTAGLIST_TAGLIST == null) { + return null; + } + try { + return (List) NBTTAGLIST_TAGLIST.get(list); + } catch (IllegalArgumentException ex) { + return null; + } catch (IllegalAccessException ex) { + return null; + } + } + + public static List getSeeds() { + return getPrivateStaticObject(ForgeHooks.class, "seedList"); + } + + public static Map getChestLoot() { + return getPrivateStaticObject(ChestGenHooks.class, "chestInfo"); + } + + public static Map getTranslations() { + return getPrivateObject( + getPrivateStaticObject(StatCollector.class, "localizedName", "field_74839_a"), + "languageList", + "field_74816_c"); + } + + public static List> getOreIdStacks() { + try { + return (List>) OREDICTIONARY_IDTOSTACK.get(null); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("ERROR - could not load ore dictionary stacks!"); + return null; + } + } + + public static List> getOreIdStacksUn() { + try { + return (List>) OREDICTIONARY_IDTOSTACKUN.get(null); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("ERROR - could not load ore dictionary stacks!"); + return null; + } + } + + public static File getAnvilFile(MinecraftServer server) { + try { + return (File) MINECRAFTSERVER_ANVILFILE.get(server); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not load anvil file!"); + return null; + } + } + + public static File getWorldDirectory(MinecraftServer server) { + if (server.isDedicatedServer()) { + return server.getFile("world"); + } else { + File worldsDir = getAnvilFile(server); + if (worldsDir == null) + return null; + + return new File(worldsDir, server.getFolderName()); + } + } + + public static int getShapedOreRecipeWidth(ShapedOreRecipe recipe) { + try { + return SHAPEDORERECIPE_WIDTH.getInt(recipe); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not load anvil file!"); + return 3; + } + } + + public static Container getCraftingContainer(InventoryCrafting inventory) { + try { + return (Container) INVENTORYCRAFTING_EVENTHANDLER.get(inventory); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not get inventory eventhandler"); + return null; + } + } + + public static EntityPlayer getCraftingSlotPlayer(SlotCrafting slot) { + try { + return (EntityPlayer) SLOTCRAFTING_PLAYER.get(slot); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not get inventory eventhandler"); + return null; + } + } + + public static StringTranslate getStringTranslateInstance() { + try { + Field field = getField(StringTranslate.class, MineTweakerObfuscation.STRINGTRANSLATE_INSTANCE); + return (StringTranslate) field.get(null); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not get string translator"); + return null; + } + } + + public static ItemStack getSeedEntrySeed(Object entry) { + try { + return (ItemStack) SEEDENTRY_SEED.get(entry); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not get SeedEntry seed"); + return null; + } + } + + public static WeightedRandom.Item constructSeedEntry(WeightedItemStack stack) { + try { + return SEEDENTRY_CONSTRUCTOR.newInstance(MineTweakerMC.getItemStack(stack.getStack()), (int) stack.getChance()); + } catch (InstantiationException ex) { + MineTweakerAPI.logError("could not construct SeedEntry"); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logError("could not construct SeedEntry"); + } catch (IllegalArgumentException ex) { + MineTweakerAPI.logError("could not construct SeedEntry"); + } catch (InvocationTargetException ex) { + MineTweakerAPI.logError("could not construct SeedEntry"); + } + + return null; + } + + public static T getPrivateStaticObject(Class cls, String... names) { + for (String name : names) { + try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return (T) field.get(null); + } catch (NoSuchFieldException ex) { + + } catch (SecurityException ex) { + + } catch (IllegalAccessException ex) { + + } + } + + return null; + } + + public static T getPrivateObject(Object object, String... names) { + Class cls = object.getClass(); + for (String name : names) { + try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return (T) field.get(object); + } catch (NoSuchFieldException ex) { + + } catch (SecurityException ex) { + + } catch (IllegalAccessException ex) { + + } + } + + return null; + } + + // ####################### + // ### Private Methods ### + // ####################### + + private static Field getField(Class cls, String... names) { + for (String name : names) { + try { + Field field = cls.getDeclaredField(name); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException ex) { + } catch (SecurityException ex) { + } + } + + return null; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerObfuscation.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerObfuscation.java new file mode 100644 index 0000000..08d1267 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerObfuscation.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.util; + +/** + * + * @author Stanneke + */ +public class MineTweakerObfuscation { + private MineTweakerObfuscation() { + } + + public static final String[] NBTTAGLIST_TAGLIST = { "tagList", "field_74747_a" }; + public static final String[] CRAFTINGMANAGER_RECIPES = { "recipes", "field_77597_b" }; + public static final String[] OREDICTIONARY_IDTOSTACK = { "idToStack" }; + public static final String[] OREDICTIONARY_IDTOSTACKUN = { "idToStackUn" }; + public static final String[] MINECRAFTSERVER_ANVILFILE = { "anvilFile", "field_71308_o" }; + public static final String[] INVENTORYCRAFTING_EVENTHANDLER = { "eventHandler", "field_70465_c" }; + public static final String[] SLOTCRAFTING_PLAYER = { "thePlayer", "field_75238_b" }; + public static final String[] STRINGTRANSLATE_INSTANCE = { "instance", "field_74817_a" }; +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerPlatformUtils.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerPlatformUtils.java new file mode 100644 index 0000000..aa211fb --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/util/MineTweakerPlatformUtils.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.util; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.relauncher.Side; + +/** + * + * @author Stan + */ +public class MineTweakerPlatformUtils { + private MineTweakerPlatformUtils() { + } + + public static Class getLivingEntityClass(String entityClassName) { + try { + Class entityClass = Class.forName(entityClassName); + if (!EntityLivingBase.class.isAssignableFrom(entityClass)) { + throw new RuntimeException("Not an entity class: " + entityClassName); + } + return (Class) entityClass; + } catch (ClassNotFoundException ex) { + throw new RuntimeException("entity class not found: " + entityClassName); + } + } + + public static String getLanguage() { + return FMLCommonHandler.instance().getSide() == Side.SERVER ? null : FMLClientHandler.instance().getCurrentLanguage(); + } + + public static boolean isLanguageActive(String lang) { + String current = getLanguage(); + return current != null && current.equals(lang); + } + + public static boolean isClient() { + return FMLCommonHandler.instance().getSide() == Side.CLIENT; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCLootRegistry.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCLootRegistry.java new file mode 100644 index 0000000..b8fe86b --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCLootRegistry.java @@ -0,0 +1,204 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.vanilla; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.WeightedItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.vanilla.ILootRegistry; +import minetweaker.api.vanilla.LootEntry; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.util.WeightedRandomChestContent; +import net.minecraftforge.common.ChestGenHooks; + +/** + * + * @author Stan + */ +public class MCLootRegistry implements ILootRegistry { + private static final Map LOOT = MineTweakerHacks.getChestLoot(); + + @Override + public void addChestLoot(String type, WeightedItemStack item) { + WeightedRandomChestContent content = new WeightedRandomChestContent( + MineTweakerMC.getItemStack(item.getStack()), + 1, + 1, + (int) item.getChance()); + MineTweakerAPI.apply(new AddLootAction(type, content)); + } + + @Override + public void addChestLoot(String type, WeightedItemStack item, int min, int max) { + WeightedRandomChestContent content = new WeightedRandomChestContent( + MineTweakerMC.getItemStack(item.getStack()), + min, + max, + (int) item.getChance()); + MineTweakerAPI.apply(new AddLootAction(type, content)); + } + + @Override + public void removeChestLoot(String type, IIngredient pattern) { + MineTweakerAPI.apply(new RemoveLootAction(type, pattern)); + } + + @Override + public List getLoot(String type) { + ChestGenHooks recipe = LOOT.get(type); + List contents = MineTweakerHacks.getPrivateObject(recipe, "contents"); + + List results = new ArrayList(); + for (WeightedRandomChestContent content : contents) { + results.add(new LootEntry(new WeightedItemStack( + MineTweakerMC.getIItemStack(content.theItemId), + content.itemWeight), + content.theMinimumChanceToGenerateItem, + content.theMaximumChanceToGenerateItem)); + } + return results; + } + + @Override + public List getLootTypes() { + Set keys = LOOT.keySet(); + List keyList = new ArrayList(); + keyList.addAll(keys); + return keyList; + } + + // ###################### + // ### Action Classes ### + // ###################### + + /** + * Ported from ModTweaker. + * + * @author JoshieJack + */ + private static class AddLootAction implements IUndoableAction { + private final String chest; + private final WeightedRandomChestContent content; + + public AddLootAction(String chest, WeightedRandomChestContent content) { + this.chest = chest; + this.content = content; + } + + @Override + public void apply() { + ChestGenHooks recipe = LOOT.get(chest); + List contents = MineTweakerHacks.getPrivateObject(recipe, "contents"); + + contents.add(content); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + ChestGenHooks recipe = LOOT.get(chest); + List contents = MineTweakerHacks.getPrivateObject(recipe, "contents"); + + contents.remove(content); + } + + public String getRecipeInfo() { + return content.theItemId.getDisplayName(); + } + + @Override + public String describe() { + return "Adding chest loot " + content.theItemId.getDisplayName() + " to loot class " + chest; + } + + @Override + public String describeUndo() { + return "Removing chest loot " + content.theItemId.getDisplayName() + " from loot class " + chest; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + /** + * Ported from ModTweaker. + * + * @author JoshieJack + */ + private static class RemoveLootAction implements IUndoableAction { + private final String chest; + private final IIngredient pattern; + private final List removed; + + public RemoveLootAction(String chest, IIngredient pattern) { + this.chest = chest; + this.pattern = pattern; + removed = new ArrayList(); + } + + @Override + public void apply() { + removed.clear(); + + ChestGenHooks recipe = LOOT.get(chest); + List contents = MineTweakerHacks.getPrivateObject(recipe, "contents"); + + for (WeightedRandomChestContent r : contents) { + if (pattern.matches(MineTweakerMC.getIItemStack(r.theItemId))) { + removed.add(r); + } + } + + for (WeightedRandomChestContent r : removed) { + contents.remove(r); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + ChestGenHooks recipe = LOOT.get(chest); + List contents = MineTweakerHacks.getPrivateObject(recipe, "contents"); + + for (WeightedRandomChestContent entry : removed) { + contents.add(entry); + } + } + + @Override + public String describe() { + return "Removing chest loot " + pattern + " for loot type " + chest; + } + + @Override + public String describeUndo() { + return "Restoring chest loot " + pattern + " for loot type " + chest + " (" + removed.size() + " entries)"; + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCSeedRegistry.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCSeedRegistry.java new file mode 100644 index 0000000..6f61973 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCSeedRegistry.java @@ -0,0 +1,150 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.vanilla; + +import java.util.ArrayList; +import java.util.List; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.api.item.IIngredient; +import minetweaker.api.item.IItemStack; +import minetweaker.api.item.WeightedItemStack; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.vanilla.ISeedRegistry; +import minetweaker.mc18.util.MineTweakerHacks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.WeightedRandom; + +/** + * + * @author Stan + */ +public class MCSeedRegistry implements ISeedRegistry { + private static final List SEEDS = MineTweakerHacks.getSeeds(); + + @Override + public void addSeed(WeightedItemStack item) { + MineTweakerAPI.apply(new AddSeedAction(item)); + } + + @Override + public void removeSeed(IIngredient pattern) { + MineTweakerAPI.apply(new RemoveSeedAction(pattern)); + } + + @Override + public List getSeeds() { + List entries = (List) SEEDS; + + List results = new ArrayList(); + for (WeightedRandom.Item entry : entries) { + results.add(new WeightedItemStack( + MineTweakerMC.getIItemStack(MineTweakerHacks.getSeedEntrySeed(entry)), + entry.itemWeight + )); + } + return results; + } + + // ###################### + // ### Action classes ### + // ###################### + + private static class AddSeedAction implements IUndoableAction { + private final IItemStack item; + private final WeightedRandom.Item entry; + + public AddSeedAction(WeightedItemStack item) { + this.item = item.getStack(); + entry = MineTweakerHacks.constructSeedEntry(item); + } + + @Override + public void apply() { + SEEDS.add(entry); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + SEEDS.remove(entry); + } + + @Override + public String describe() { + return "Adding seed entry " + item; + } + + @Override + public String describeUndo() { + return "Removing seed entry " + item; + } + + @Override + public Object getOverrideKey() { + return null; + } + } + + private static class RemoveSeedAction implements IUndoableAction { + private final IIngredient pattern; + private final List removed; + + public RemoveSeedAction(IIngredient ingredient) { + this.pattern = ingredient; + removed = new ArrayList(); + } + + @Override + public void apply() { + removed.clear(); + + for (Object entry : SEEDS) { + ItemStack itemStack = MineTweakerHacks.getSeedEntrySeed(entry); + if (pattern.matches(MineTweakerMC.getIItemStack(itemStack))) { + removed.add(entry); + } + } + + for (Object entry : removed) { + SEEDS.remove(entry); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (Object entry : removed) { + SEEDS.add(entry); + } + } + + @Override + public String describe() { + return "Removing seeds " + pattern; + } + + @Override + public String describeUndo() { + return "Restoring seeds " + pattern + " (" + removed.size() + " entries)"; + } + + @Override + public Object getOverrideKey() { + return null; + } + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCVanilla.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCVanilla.java new file mode 100644 index 0000000..0a9007b --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/vanilla/MCVanilla.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.vanilla; + +import minetweaker.api.vanilla.ILootRegistry; +import minetweaker.api.vanilla.ISeedRegistry; +import minetweaker.api.vanilla.IVanilla; + +/** + * + * @author Stan + */ +public class MCVanilla implements IVanilla { + private final MCLootRegistry lootRegistry = new MCLootRegistry(); + private final MCSeedRegistry seedRegistry = new MCSeedRegistry(); + + @Override + public ILootRegistry getLoot() { + return lootRegistry; + } + + @Override + public ISeedRegistry getSeeds() { + return seedRegistry; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCBiome.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCBiome.java new file mode 100644 index 0000000..e1a85f1 --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCBiome.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.world; + +import minetweaker.api.world.IBiome; +import net.minecraft.world.biome.BiomeGenBase; + +/** + * + * @author Stan + */ +public class MCBiome implements IBiome { + private final BiomeGenBase biome; + + public MCBiome(BiomeGenBase biome) { + this.biome = biome; + } + + @Override + public String getName() { + return biome.biomeName; + } +} diff --git a/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCDimension.java b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCDimension.java new file mode 100644 index 0000000..e822cbb --- /dev/null +++ b/MineTweaker3-MC18-Main/src/main/java/minetweaker/mc18/world/MCDimension.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc18.world; + +import minetweaker.api.block.IBlock; +import minetweaker.api.minecraft.MineTweakerMC; +import minetweaker.api.world.IDimension; +import net.minecraft.util.BlockPos; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class MCDimension implements IDimension { + private final World world; + + public MCDimension(World world) { + this.world = world; + } + + @Override + public boolean isDay() { + return world.isDaytime(); + } + + @Override + public int getBrightness(int x, int y, int z) { + return (int) world.getLightBrightness(new BlockPos(x, y, z)); + } + + @Override + public IDimension getDimension() { + return this; + } + + @Override + public IBlock getBlock(int x, int y, int z) { + return MineTweakerMC.getBlock(world, x, y, z); + } +} diff --git a/MineTweaker3-MC18-Main/src/main/resources/assets/minetweaker/logo.png b/MineTweaker3-MC18-Main/src/main/resources/assets/minetweaker/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..674382e31e87dcb701bc76ebb1fba5aad1e13b48 GIT binary patch literal 21583 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%0DJ4FZk2jt}6##lUY5Q%5qw<&z#}$WK+ngW zIp8|5cP~BzK0nCCmIEv-BDE8_x+Dk!kUbm!7e~5?0Uz^CCIA$8Fij0YPdkTo7De#1CWN#6cy=Fs zywk^A1cMDU*WQ?v@sfOyj#)Plz|5I5#fY3n{#oJk;ln|`sQ@$)$T$Fhzp#J5==qof zfH**skM#jDK0S5QjlBUSIe@tUu#W&xz$qzK8_DC$f$<7CLrI+!igV*LU{SCwojROq zJ{EwPwy*|Rs*T&RsIN%@Kp1$Rv>0;I=KmXaU=YFCrv&+Qb}#+;)lzc-d}W}y_Qu4V z!ekDZeUJ~}7Hg7xY|4ZUlt8}h{j~`IkJxYt zmmul{Yza^(Qg`}PXYHnNiunALM%OQbgY{$y3?g`Yc~f%{v^G$EWn)sDKnylXz+_91 zKYR9U6Y^!W5Asa~pou^V0Dt55YDV(kr)}G|i^(GR49+gZ9smi^5#(!Y+#^sG0RBc# z#~@A|`)*YL{&w2jL@Z3jbZGe=6(Ama7h!Y|t^?0W0RU=rkDiV}AGwRCeBP1(a34RE ziBT=YTzp*surA_UunO2qxe1G)p+yn=dXq%(!Q(y61<=Vr_mz!_Nd!4nRaHU&ARm)2 z(Xj9W`{&7?Uz-3VaO|Evdzt)WA`ZYJ4wZbFnx!Yjb1vP3;yA#==1|dw-l_u6VHp~x zn&V~^ssMnxtN?Z&foL#A9cvbFXES_`umA=Dz@&>2Ktuf>y}B*|V0=K16-WVKz(|fJ z;(v$(4nKf&+*kcc?ZxIL@ zMPR(Onj{}_fFwUNGxKtEXyBQIgoN|D0Fo2fR~t*c@ZVyv#sZn>(O7(nV9=L?z1jo- zvB(~eRY`JS2*8SjNgn~5t4lP7h zpw0_a0RdpX09*mKfR*oq`_PU-(5P7h#1;@!U4h}RpZ2R~5d;Vk{QjZoTtdcbjpT7d;X`t~O0?NR_MR;&;^XU>^3SEPl*h76@&fBjXs1CWmuJGh-1;sC7I zH;{iZBO@b?KcWf4j*-Ne+`W7Er@Wl(zcvtmSrq|*k}aZmb>t6#OQ=f#>I4zEi=s&9 zE9MGV5&%d@3S=*fEQo) zx*L=A2S`KDM=2KrHt5Y#0Jw7&_Rw3td-NdkZJY0ncJFpFE7;*&t_30FggEN+53g4Mm@tY#q=N8XVm<+40ayof0bt%A zqI(c!UJ3?~5@LHdZ1xC|$mbydSOB;Hhc28Jy1R=F8?bIaz|mN0#yLH0+h2C+*7)4B z4_RHh(IUVWc5f}YO(p+XT>PsxCTkr-Y@>=rmnidMgGCSv0l*qyz8*@rFz?u%1FLs6 z@?RElfEw*HiVUYVZw(o+2to)g?`=G(L#unAPWH7unGtCJr@08^&LY<$0GNKcW2u-i ztegjfK-kO@76B`0B$1d!qmm_`M{tK#Ll_3e0t^fZqh=vrha0eK*FqeJJ$sD1c4z|7 zh;K*jTi)~M-FMt}(#opA)1KXLS@_Z{s~>Nq2;N?DyGs5=hAl4oRT~p#!}zmr-@Z(^ zX8N&>x+((X^S}J^tC;wK#hKb`ApbusgneSNh=KfS!)fbkRtyNio{`L$)VX!jXXCwX zzR3*galw)Rv?*SkqcdrU<|E94K)l|!cQ0WHD$N9-0>nxp%>{sEVuzgmW6as*M zXRD#c32ur2JUu+f*}6S-Z{Okq|E6yk=z)_AztMh0bpyB8=2@M(5h8eJaRZfnU^su~ zwKpcN1O!3^AQ!zJGkjDLKpe;b(DQNs9Rz@g{I8RIwZS65HOLrYlABH2rvrO-{ECCZ zzf=K8AQ^kQ00kuJF4XPZApmc0 zZ?dzqqmCUr{&oB9ccAk2G=7WWH{rM8H{!S6f?oNzMewm@5q!R(fxf4K0s9zN9|JIN zAP_`?P?&s(0CW6sn^fE}Q~G?3{Nw+We6`6KL0E*l?znZbU5A$YJZ##0!#(>F6MNa5 zXv7C<$yY^fOT(@Z0GnyiqnUgJcxqq=0l@rYlO}cjohkqXjSijezuc--t4oa=H%5=& z$C!-YVE8TgO;-@{k3?W*3BKLioZd9-U1-4jjO!l?md#uQ==mTYB9P=G`bV{|9>{N? z0B+${Ml(h)M%q1%Z+oasv%8-Tbm(v@+`03g==F#N@Q9^fatAWpunNTEdEA{0rC_{f z3D~6@gF7G{WnApP3ttY?-}U~c0uCLuE#6@nP%r{SiP!*FelL!1b?=>j-re{%2;0UYve)wbmLgC@u?WYg@Zk43 zanJA2^4{~eHf(UZL4#Wk@b{a^$YUVg>d3(N$M3;!!*2xed(kU#4FFt|+n*e7)ssc= zt+@!!|1!5u5uAD9e)??t{VMU2{A&yPuBE9Wc;u0l`ppzpu3V{;pOTV-Km$Kc5@}8% z(28*%<9-Iz*@n^908Cv%nX-ccn9PWa2%vu5g#l~On^DeK_&}?B4s)+P730zEQX-2W z(Wko!61=gaW*7F`)>pg+U*uJ0Hll9o}B~Li~JqX z;}0_yFiII=4D@<@UwnW34*VwkHUPg9zZt(B*8srvz;$UkzqEnZvAI^KRS_Ip+=foS znMc2zC@>MhCz~43yQ{A7_W;a(?d9ID5u(v^t*M zfqXuZ0ReEJDFD9@0RV6VARfTdYbg>t7DuZlchT*)HpGnEL-=gNcffbScfxm5zpMN` z0Dd2SD}FP6JAmte>!FKfQf`BQSLa%tQAGe8p4Nz7p3|6Kn$?J2nI)!rnG=s&*1fR2 z(h5Al`qt`luGT=hG}ACu(nL263k&mMAuP$kkq0aSUb_|n;Evq<|K(

1~+ttL5k5>^+S z!M%b%xZdj$0uF{xGAAEqU7GY34D;S@iF`}FygrCGhZI5#V?dL;ZeQH;-o}T*oI9V1 za_@Fd6#zE1aAF^ih&_~J2l)2d1c1#Ukm|K>dCxf(7ZzE_9uMO2S^tE16_!Pi$s%A& zpzchf0!!3iosGMbS-rfv#wvi{qsAJl2t>+xJq;p2@CG5^BqmmXQI`;;*}oQ(Utg9$ z0zr=pBZ>i%7Po1B_fhV_XMFA2UyuS2b$kXkRK}RSxd3oGSJVJ(wE_-^KZI0_McrI+zE15Jg?VUS08kxjPrmoMN|(GC3)rqN)jcJDw!&wCZsZO4R9?0To+3w z9M@8$i40t0bVNz=O(VKKniPu^41S2TDpEKAeykLbv^3J%NQ)!IBn2fQFQx=8Fn|jW zLHIKA@3^($%w~7p@tl487T>Z9`0f5?cmH