diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..412eeda7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6cbe13fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.class +*.log +*.ctxt +.mtj.tmp/ +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +hs_err_pid* +.gradle +**/build/ +!src/**/build/ +gradle-app.setting +!gradle-wrapper.jar +.gradletasknamecache +.idea/ +run/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..1b6a0a7e --- /dev/null +++ b/build.gradle @@ -0,0 +1,56 @@ +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "https://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' + +version = modVersion +group= modGroup +archivesBaseName = modBaseName + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +minecraft { + version = project.forgeVersion +} + +dependencies { +} + +processResources +{ + inputs.property "version", project.version + inputs.property "mcversion", project.minecraft.version + + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + expand 'version':project.version, 'mcversion':project.minecraft.version + } + + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + appendix = 'deobf' +} + +artifacts { + archives deobfJar +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..2ee1be2f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +modGroup = com.gtnewhorizon +modVersion = 1.0.0 +modBaseName = StructureLib + +forgeVersion = 1.7.10-10.13.4.1614-1.7.10 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..99340b4a Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..82b0d2f5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Jan 04 01:52:14 CET 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..91a7e269 --- /dev/null +++ b/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/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java new file mode 100644 index 00000000..4fd9f9e6 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java @@ -0,0 +1,18 @@ +package com.gtnewhorizon.structurelib; + +import com.gtnewhorizon.structurelib.proxy.CommonProxy; +import com.gtnewhorizon.structurelib.util.XSTR; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.SidedProxy; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod(modid = StructureLib.MOD_ID, name = "StructureLib", version = "${version}", acceptableRemoteVersions = "*", canBeDeactivated = false) +public class StructureLib { + public static boolean DEBUG_MODE = true; + public static Logger LOGGER = LogManager.getLogger("StructureLib"); + public static final String MOD_ID = "structurelib"; + @SidedProxy(serverSide = "com.gtnewhorizon.structurelib.proxy.CommonProxy", clientSide = "com.gtnewhorizon.structurelib.proxy.ClientProxy") + public static CommonProxy proxy; + public static final XSTR RANDOM = new XSTR(); +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentLimits.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentLimits.java new file mode 100644 index 00000000..480a3188 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentLimits.java @@ -0,0 +1,183 @@ +package com.gtnewhorizon.structurelib.alignment; + +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import net.minecraftforge.common.util.ForgeDirection; + +import java.util.Arrays; +import java.util.Optional; +import java.util.Random; +import java.util.function.Function; + +import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT; + +public class AlignmentLimits implements IAlignmentLimits { + + protected final boolean[] validStates=new boolean[STATES_COUNT]; + + public AlignmentLimits() { + allowAll(); + } + + AlignmentLimits allowAll(){ + Arrays.fill(validStates,true); + return this; + } + + AlignmentLimits denyAll(){ + Arrays.fill(validStates,false); + return this; + } + + AlignmentLimits randomAll(Random random){ + for (int i = 0; i < validStates.length; i++) { + validStates[i]=random.nextBoolean(); + } + return this; + } + + AlignmentLimits deny(ExtendedFacing... deny){ + if(deny!=null){ + for (ExtendedFacing extendedFacing : deny) { + validStates[extendedFacing.getIndex()]=false; + } + } + return this; + } + + AlignmentLimits allow(ExtendedFacing... allow){ + if(allow!=null){ + for (ExtendedFacing extendedFacing : allow) { + validStates[extendedFacing.getIndex()]=false; + } + } + return this; + } + + AlignmentLimits deny(ForgeDirection... deny){ + if(deny!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (ForgeDirection direction : deny) { + if (value.getDirection() == direction) { + validStates[value.getIndex()] = false; + break; + } + } + } + } + return this; + } + + AlignmentLimits allow(ForgeDirection... allow){ + if(allow!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (ForgeDirection direction : allow) { + if (value.getDirection() == direction) { + validStates[value.getIndex()] = true; + break; + } + } + } + } + return this; + } + + AlignmentLimits deny(Rotation... deny){ + if(deny!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (Rotation rotation : deny) { + if (value.getRotation() == rotation) { + validStates[value.getIndex()] = false; + break; + } + } + } + } + return this; + } + + AlignmentLimits allow(Rotation... allow){ + if(allow!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (Rotation rotation : allow) { + if (value.getRotation() == rotation) { + validStates[value.getIndex()] = true; + break; + } + } + } + } + return this; + } + + AlignmentLimits deny(Flip... deny){ + if(deny!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (Flip flip : deny) { + if (value.getFlip() == flip) { + validStates[value.getIndex()] = false; + break; + } + } + } + } + return this; + } + + AlignmentLimits allow(Flip... allow){ + if(allow!=null){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + for (Flip flip : allow) { + if (value.getFlip() == flip) { + validStates[value.getIndex()] = true; + break; + } + } + } + } + return this; + } + + AlignmentLimits predicateApply(Function> predicate){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + predicate.apply(value).ifPresent(bool->validStates[value.getIndex()]=bool); + } + return this; + } + + AlignmentLimits ensureDuplicates(){ + for (ExtendedFacing value : ExtendedFacing.VALUES) { + if(validStates[value.getIndex()]){ + validStates[value.getDuplicate().getIndex()]=true; + } + } + return this; + } + + /** + * Prefers rotation over flip, so both flip will get translated to opposite rotation and no flip + * @param flip the preferred flip to be used Horizontal or vertical + * @return this + */ + AlignmentLimits ensureNoDuplicates(Flip flip){ + if(flip==Flip.BOTH||flip==Flip.NONE){ + throw new IllegalArgumentException("Preffered Flip must be Horizontal or Vertical"); + } + flip=flip.getOpposite(); + for (ExtendedFacing value : ExtendedFacing.VALUES) { + if(validStates[value.getIndex()]){ + if(value.getFlip()==Flip.BOTH || value.getFlip()==flip){ + validStates[value.getIndex()]=false; + validStates[value.getDuplicate().getIndex()]=true; + } + } + } + return this; + } + + @Override + public boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip) { + return validStates[IAlignment.getAlignmentIndex(direction,rotation,flip)]; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentMessage.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentMessage.java new file mode 100644 index 00000000..503aaff7 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentMessage.java @@ -0,0 +1,130 @@ +package com.gtnewhorizon.structurelib.alignment; + +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import io.netty.buffer.ByteBuf; +import net.minecraft.nbt.NBTTagCompound; + +public class AlignmentMessage implements IMessage { + int mPosX; + int mPosY; + int mPosZ; + int mPosD; + int mAlign; + + public AlignmentMessage() { + } + + @Override + public void fromBytes(ByteBuf pBuffer) { + NBTTagCompound tTag = ByteBufUtils.readTag(pBuffer); + mPosX = tTag.getInteger("posx"); + mPosY = tTag.getInteger("posy"); + mPosZ = tTag.getInteger("posz"); + mPosD = tTag.getInteger("posd"); + mAlign = tTag.getInteger("rotf"); + } + + @Override + public void toBytes(ByteBuf pBuffer) { + NBTTagCompound tFXTag = new NBTTagCompound(); + tFXTag.setInteger("posx", mPosX); + tFXTag.setInteger("posy", mPosY); + tFXTag.setInteger("posz", mPosZ); + tFXTag.setInteger("posd", mPosD); + tFXTag.setInteger("rotf", mAlign); + + ByteBufUtils.writeTag(pBuffer, tFXTag); + } +/* + private AlignmentMessage(GT_MetaTileEntity_MultiblockBase_EM metaTile) { + IGregTechTileEntity base=metaTile.getBaseMetaTileEntity(); + mPosX=base.getXCoord(); + mPosY=base.getYCoord(); + mPosZ=base.getZCoord(); + mPosD=base.getWorld().provider.dimensionId; + mAlign =metaTile.getExtendedFacing().getIndex(); + } + + private AlignmentMessage(World world, int x, int y, int z, IAlignment front) { + mPosX=x; + mPosY=y; + mPosZ=z; + mPosD=world.provider.dimensionId; + mAlign =front.getExtendedFacing().getIndex(); + } + + public static class AlignmentQuery extends AlignmentMessage { + public AlignmentQuery() { + } + + public AlignmentQuery(GT_MetaTileEntity_MultiblockBase_EM metaTile) { + super(metaTile); + } + + public AlignmentQuery(World world, int x, int y, int z, IAlignment front) { + super(world,x,y,z,front); + } + } + + public static class AlignmentData extends AlignmentMessage { + public AlignmentData() { + } + + private AlignmentData(AlignmentQuery query){ + mPosX=query.mPosX; + mPosY=query.mPosY; + mPosZ=query.mPosZ; + mPosD=query.mPosD; + mAlign =query.mAlign; + } + + public AlignmentData(GT_MetaTileEntity_MultiblockBase_EM metaTile) { + super(metaTile); + } + + public AlignmentData(World world, int x, int y, int z, IAlignment front) { + super(world,x,y,z,front); + } + } + + public static class ClientHandler extends AbstractClientMessageHandler { + @Override + public IMessage handleClientMessage(EntityPlayer pPlayer, AlignmentData pMessage, MessageContext pCtx) { + if(pPlayer.worldObj.provider.dimensionId==pMessage.mPosD){ + TileEntity te=pPlayer.worldObj.getTileEntity(pMessage.mPosX,pMessage.mPosY,pMessage.mPosZ); + if(te instanceof IGregTechTileEntity){ + IMetaTileEntity meta = ((IGregTechTileEntity) te).getMetaTileEntity(); + if(meta instanceof IAlignment){ + ((IAlignment) meta).setExtendedFacing(ExtendedFacing.byIndex(pMessage.mAlign)); + } + }else if (te instanceof IAlignment){ + ((IAlignment) te).setExtendedFacing(ExtendedFacing.byIndex(pMessage.mAlign)); + } + } + return null; + } + } + + public static class ServerHandler extends AbstractServerMessageHandler { + @Override + public IMessage handleServerMessage(EntityPlayer pPlayer, AlignmentQuery pMessage, MessageContext pCtx) { + World world= DimensionManager.getWorld(pMessage.mPosD); + if(world!=null) { + TileEntity te = world.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ); + if (te instanceof IGregTechTileEntity) { + IMetaTileEntity meta = ((IGregTechTileEntity) te).getMetaTileEntity(); + if (meta instanceof IAlignment) { + pMessage.mAlign =((IAlignment) meta).getExtendedFacing().getIndex(); + return new AlignmentData(pMessage); + } + } else if (te instanceof IAlignment) { + pMessage.mAlign =((IAlignment) te).getExtendedFacing().getIndex(); + return new AlignmentData(pMessage); + } + } + return null; + } + } + */ +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentUtility.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentUtility.java new file mode 100644 index 00000000..27afef44 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/AlignmentUtility.java @@ -0,0 +1,43 @@ +package com.gtnewhorizon.structurelib.alignment; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.FakePlayer; + +public class AlignmentUtility { + private AlignmentUtility(){ + } + + public static boolean handle(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ){ + TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if(tTileEntity==null || aPlayer instanceof FakePlayer) { + return aPlayer instanceof EntityPlayerMP; + } + if (aPlayer instanceof EntityPlayerMP) { + /* TODO Make GT tile entity implement this interface and delegate all functionality to MTE + if (tTileEntity instanceof IGregTechTileEntity) { + IMetaTileEntity metaTE = ((IGregTechTileEntity) tTileEntity).getMetaTileEntity(); + if (metaTE instanceof IAlignmentProvider) { + IAlignment alignment = ((IAlignmentProvider) metaTE).getAlignment(); + if(aPlayer.isSneaking()){ + alignment.toolSetFlip(null); + }else { + alignment.toolSetRotation(null); + } + return true; + } + } else */ if (tTileEntity instanceof IAlignmentProvider) { + IAlignment alignment = ((IAlignmentProvider) tTileEntity).getAlignment(); + if(aPlayer.isSneaking()){ + alignment.toolSetFlip(null); + }else { + alignment.toolSetRotation(null); + } + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignment.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignment.java new file mode 100644 index 00000000..ae343022 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignment.java @@ -0,0 +1,185 @@ +package com.gtnewhorizon.structurelib.alignment; + +import com.gtnewhorizon.structurelib.alignment.enumerable.Direction; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import net.minecraftforge.common.util.ForgeDirection; + +public interface IAlignment extends IAlignmentLimits,IAlignmentProvider { + int DIRECTIONS_COUNT= Direction.VALUES.length; + int ROTATIONS_COUNT= Rotation.VALUES.length; + int FLIPS_COUNT= Flip.VALUES.length; + int STATES_COUNT = ExtendedFacing.VALUES.length; + + ExtendedFacing getExtendedFacing(); + + void setExtendedFacing(ExtendedFacing alignment); + + IAlignmentLimits getAlignmentLimits(); + + void setAlignmentLimits(IAlignmentLimits limits); + + @Override + default IAlignment getAlignment(){ + return this; + } + + static int getAlignmentIndex(ForgeDirection direction, Rotation rotation, Flip flip){ + return (direction.ordinal()*ROTATIONS_COUNT+rotation.getIndex())*FLIPS_COUNT+flip.getIndex(); + } + + default ForgeDirection getDirection(){ + return getExtendedFacing().getDirection(); + } + + default void setDirection(ForgeDirection direction){ + setExtendedFacing(getExtendedFacing().with(direction)); + } + + default Rotation getRotation(){ + return getExtendedFacing().getRotation(); + } + + default void setRotation(Rotation rotation){ + setExtendedFacing(getExtendedFacing().with(rotation)); + } + + default Flip getFlip(){ + return getExtendedFacing().getFlip(); + } + + default void setFlip(Flip flip){ + setExtendedFacing(getExtendedFacing().with(flip)); + } + + default boolean toolSetDirection(ForgeDirection direction){ + if(direction==null || direction==ForgeDirection.UNKNOWN){ + for (int i = 0,j=getDirection().ordinal()+1, valuesLength = Direction.VALUES.length; i < valuesLength; i++) { + if(toolSetDirection(Direction.VALUES[j%valuesLength].getForgeDirection())){ + return true; + } + } + }else { + for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) { + if(checkedSetExtendedFacing(extendedFacing)){ + return true; + } + } + } + return false; + } + + default boolean checkedSetDirection(ForgeDirection direction){ + if (isNewDirectionValid(direction)){ + setDirection(direction); + return true; + } + return false; + } + + default boolean canSetToDirectionAny(ForgeDirection direction){ + for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) { + if(isNewExtendedFacingValid(extendedFacing)){ + return true; + } + } + return false; + } + + default boolean toolSetRotation(Rotation rotation) { + if(rotation==null){ + int flips = Flip.VALUES.length; + int rotations = Rotation.VALUES.length; + for (int ii = 0,jj=getFlip().ordinal(); ii < flips; ii++) { + for (int i = 0,j=getRotation().ordinal()+1; i < rotations; i++) { + if(checkedSetExtendedFacing(ExtendedFacing.of(getDirection(),Rotation.VALUES[j%rotations],Flip.VALUES[jj%flips]))){ + return true; + } + } + } + return false; + }else { + return checkedSetRotation(rotation); + } + } + + default boolean checkedSetRotation(Rotation rotation){ + if (isNewRotationValid(rotation)){ + setRotation(rotation); + return true; + } + return false; + } + + default boolean toolSetFlip(Flip flip){ + if(flip==null){ + for (int i = 0,j=getFlip().ordinal()+1, valuesLength = Flip.VALUES.length; i < valuesLength; i++) { + if(toolSetFlip(Flip.VALUES[j%valuesLength])){ + return true; + } + } + return false; + }else { + return checkedSetFlip(flip); + } + } + + default boolean checkedSetFlip(Flip flip){ + if (isNewFlipValid(flip)){ + setFlip(flip); + return true; + } + return false; + } + + default boolean toolSetExtendedFacing(ExtendedFacing extendedFacing){ + if(extendedFacing==null){ + for (int i = 0,j=getExtendedFacing().ordinal()+1, valuesLength = ExtendedFacing.VALUES.length; i < valuesLength; i++) { + if(toolSetExtendedFacing(ExtendedFacing.VALUES[j%valuesLength])){ + return true; + } + } + return false; + }else { + return checkedSetExtendedFacing(extendedFacing); + } + } + + default boolean checkedSetExtendedFacing(ExtendedFacing alignment){ + if (isNewExtendedFacingValid(alignment)){ + setExtendedFacing(alignment); + return true; + } + return false; + } + + default boolean isNewDirectionValid(ForgeDirection direction) { + return isNewExtendedFacingValid(direction,getRotation(),getFlip()); + } + + default boolean isNewRotationValid(Rotation rotation){ + return isNewExtendedFacingValid(getDirection(),rotation,getFlip()); + } + + default boolean isNewFlipValid(Flip flip){ + return isNewExtendedFacingValid(getDirection(),getRotation(),flip); + } + + default boolean isExtendedFacingValid() { + return isNewExtendedFacingValid(getDirection(),getRotation(),getFlip()); + } + + @Override + default boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip){ + return getAlignmentLimits().isNewExtendedFacingValid(direction, rotation, flip); + } + + @Override + default boolean isNewExtendedFacingValid(ExtendedFacing alignment){ + return getAlignmentLimits().isNewExtendedFacingValid( + alignment.getDirection(), + alignment.getRotation(), + alignment.getFlip()); + } +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentLimits.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentLimits.java new file mode 100644 index 00000000..258b6145 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentLimits.java @@ -0,0 +1,19 @@ +package com.gtnewhorizon.structurelib.alignment; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import net.minecraftforge.common.util.ForgeDirection; + +public interface IAlignmentLimits { + + IAlignmentLimits UNLIMITED= (direction, rotation, flip) -> true; + + boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip); + + default boolean isNewExtendedFacingValid(ExtendedFacing alignment){ + return isNewExtendedFacingValid( + alignment.getDirection(), + alignment.getRotation(), + alignment.getFlip()); + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentProvider.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentProvider.java new file mode 100644 index 00000000..d8a1ac98 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentProvider.java @@ -0,0 +1,5 @@ +package com.gtnewhorizon.structurelib.alignment; + +public interface IAlignmentProvider { + IAlignment getAlignment(); +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/IntegerAxisSwap.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/IntegerAxisSwap.java new file mode 100644 index 00000000..ea546c3f --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/IntegerAxisSwap.java @@ -0,0 +1,84 @@ +package com.gtnewhorizon.structurelib.alignment; + +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import com.gtnewhorizon.structurelib.alignment.enumerable.Direction; +import net.minecraft.util.Vec3; +import net.minecraftforge.common.util.ForgeDirection; + +import static java.lang.Math.abs; + +public class IntegerAxisSwap { + private final Vec3Impl forFirstAxis; + private final Vec3Impl forSecondAxis; + private final Vec3Impl forThirdAxis; + + public IntegerAxisSwap(ForgeDirection forFirstAxis, ForgeDirection forSecondAxis, ForgeDirection forThirdAxis) { + this.forFirstAxis = Direction.getAxisVector(forFirstAxis); + this.forSecondAxis = Direction.getAxisVector(forSecondAxis); + this.forThirdAxis = Direction.getAxisVector(forThirdAxis); + if(abs(this.forFirstAxis.get0())+abs(this.forSecondAxis.get0())+abs(this.forThirdAxis.get0())!=1 || + abs(this.forFirstAxis.get1())+abs(this.forSecondAxis.get1())+abs(this.forThirdAxis.get1())!=1 || + abs(this.forFirstAxis.get2())+abs(this.forSecondAxis.get2())+abs(this.forThirdAxis.get2())!=1){ + throw new IllegalArgumentException("Axis are overlapping/missing! "+ + forFirstAxis.name()+" "+ + forSecondAxis.name()+" "+ + forThirdAxis.name()); + } + } + + public Vec3Impl translate(Vec3Impl point){ + return new Vec3Impl( + forFirstAxis.get0()*point.get0() +forFirstAxis.get1()*point.get1() +forFirstAxis.get2()*point.get2(), + forSecondAxis.get0()*point.get0()+forSecondAxis.get1()*point.get1()+forSecondAxis.get2()*point.get2(), + forThirdAxis.get0()*point.get0() +forThirdAxis.get1()*point.get1() +forThirdAxis.get2()*point.get2() + ); + } + + public Vec3Impl inverseTranslate(Vec3Impl point){ + return new Vec3Impl( + forFirstAxis.get0()*point.get0()+forSecondAxis.get0()*point.get1()+forThirdAxis.get0()*point.get2(), + forFirstAxis.get1()*point.get0()+forSecondAxis.get1()*point.get1()+forThirdAxis.get1()*point.get2(), + forFirstAxis.get2()*point.get0()+forSecondAxis.get2()*point.get1()+forThirdAxis.get2()*point.get2() + ); + } + + public Vec3 translate(Vec3 point){ + return Vec3.createVectorHelper( + forFirstAxis.get0()*point.xCoord +forFirstAxis.get1()*point.yCoord +forFirstAxis.get2()*point.zCoord, + forSecondAxis.get0()*point.xCoord+forSecondAxis.get1()*point.yCoord+forSecondAxis.get2()*point.zCoord, + forThirdAxis.get0()*point.xCoord +forThirdAxis.get1()*point.yCoord +forThirdAxis.get2()*point.zCoord + ); + } + + public Vec3 inverseTranslate(Vec3 point){ + return Vec3.createVectorHelper( + forFirstAxis.get0()*point.xCoord+forSecondAxis.get0()*point.yCoord+forThirdAxis.get0()*point.zCoord, + forFirstAxis.get1()*point.xCoord+forSecondAxis.get1()*point.yCoord+forThirdAxis.get1()*point.zCoord, + forFirstAxis.get2()*point.xCoord+forSecondAxis.get2()*point.yCoord+forThirdAxis.get2()*point.zCoord + ); + } + + public void translate(int[] point,int[] out){ + out[0]=forFirstAxis.get0()*point[0] +forFirstAxis.get1()*point[1] +forFirstAxis.get2()*point[2]; + out[1]=forSecondAxis.get0()*point[0]+forSecondAxis.get1()*point[1]+forSecondAxis.get2()*point[2]; + out[2]=forThirdAxis.get0()*point[0] +forThirdAxis.get1()*point[1] +forThirdAxis.get2()*point[2]; + } + + public void inverseTranslate(int[] point,int[] out){ + out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2]; + out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2]; + out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.get2()*point[2]; + } + + public void translate(double[] point,double[] out){ + out[0]=forFirstAxis.get0()*point[0] +forFirstAxis.get1()*point[1] +forFirstAxis.get2()*point[2]; + out[1]=forSecondAxis.get0()*point[0]+forSecondAxis.get1()*point[1]+forSecondAxis.get2()*point[2]; + out[2]=forThirdAxis.get0()*point[0] +forThirdAxis.get1()*point[1] +forThirdAxis.get2()*point[2]; + } + + public void inverseTranslate(double[] point,double[] out){ + out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2]; + out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2]; + out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.get2()*point[2]; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java new file mode 100644 index 00000000..5aa23028 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java @@ -0,0 +1,34 @@ +package com.gtnewhorizon.structurelib.alignment.enumerable; + +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import net.minecraftforge.common.util.ForgeDirection; + +public enum Direction { + DOWN(ForgeDirection.DOWN), + UP(ForgeDirection.UP), + NORTH(ForgeDirection.NORTH), + SOUTH(ForgeDirection.SOUTH), + WEST(ForgeDirection.WEST), + EAST(ForgeDirection.EAST); + + private final ForgeDirection forgeDirection; + private final Vec3Impl axisVector; + public static final Direction[] VALUES=values(); + + Direction(ForgeDirection forgeDirection) { + this.forgeDirection = forgeDirection; + axisVector=new Vec3Impl(forgeDirection.offsetX,forgeDirection.offsetY,forgeDirection.offsetZ); + } + + public ForgeDirection getForgeDirection() { + return forgeDirection; + } + + public Vec3Impl getAxisVector() { + return axisVector; + } + + public static Vec3Impl getAxisVector(ForgeDirection forgeDirection){ + return VALUES[forgeDirection.ordinal()].axisVector; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java new file mode 100644 index 00000000..376d0d7f --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java @@ -0,0 +1,358 @@ +package com.gtnewhorizon.structurelib.alignment.enumerable; + +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap; +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import net.minecraft.util.Vec3; +import net.minecraftforge.common.util.ForgeDirection; + +import java.util.*; + +import static com.gtnewhorizon.structurelib.alignment.IAlignment.FLIPS_COUNT; +import static com.gtnewhorizon.structurelib.alignment.IAlignment.ROTATIONS_COUNT; +import static java.lang.Math.abs; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toMap; + +public enum ExtendedFacing { + DOWN_NORMAL_NONE("down normal none"), + DOWN_NORMAL_HORIZONTAL("down normal horizontal"), + DOWN_NORMAL_VERTICAL("down normal vertical"), + DOWN_NORMAL_BOTH("down normal both"), + DOWN_CLOCKWISE_NONE("down clockwise none"), + DOWN_CLOCKWISE_HORIZONTAL("down clockwise horizontal"), + DOWN_CLOCKWISE_VERTICAL("down clockwise vertical"), + DOWN_CLOCKWISE_BOTH("down clockwise both"), + DOWN_UPSIDE_DOWN_NONE("down upside down none"), + DOWN_UPSIDE_DOWN_HORIZONTAL("down upside down horizontal"), + DOWN_UPSIDE_DOWN_VERTICAL("down upside down vertical"), + DOWN_UPSIDE_DOWN_BOTH("down upside down both"), + DOWN_COUNTER_CLOCKWISE_NONE("down counter clockwise none"), + DOWN_COUNTER_CLOCKWISE_HORIZONTAL("down counter clockwise horizontal"), + DOWN_COUNTER_CLOCKWISE_VERTICAL("down counter clockwise vertical"), + DOWN_COUNTER_CLOCKWISE_BOTH("down counter clockwise both"), + UP_NORMAL_NONE("up normal none"), + UP_NORMAL_HORIZONTAL("up normal horizontal"), + UP_NORMAL_VERTICAL("up normal vertical"), + UP_NORMAL_BOTH("up normal both"), + UP_CLOCKWISE_NONE("up clockwise none"), + UP_CLOCKWISE_HORIZONTAL("up clockwise horizontal"), + UP_CLOCKWISE_VERTICAL("up clockwise vertical"), + UP_CLOCKWISE_BOTH("up clockwise both"), + UP_UPSIDE_DOWN_NONE("up upside down none"), + UP_UPSIDE_DOWN_HORIZONTAL("up upside down horizontal"), + UP_UPSIDE_DOWN_VERTICAL("up upside down vertical"), + UP_UPSIDE_DOWN_BOTH("up upside down both"), + UP_COUNTER_CLOCKWISE_NONE("up counter clockwise none"), + UP_COUNTER_CLOCKWISE_HORIZONTAL("up counter clockwise horizontal"), + UP_COUNTER_CLOCKWISE_VERTICAL("up counter clockwise vertical"), + UP_COUNTER_CLOCKWISE_BOTH("up counter clockwise both"), + NORTH_NORMAL_NONE("north normal none"), + NORTH_NORMAL_HORIZONTAL("north normal horizontal"), + NORTH_NORMAL_VERTICAL("north normal vertical"), + NORTH_NORMAL_BOTH("north normal both"), + NORTH_CLOCKWISE_NONE("north clockwise none"), + NORTH_CLOCKWISE_HORIZONTAL("north clockwise horizontal"), + NORTH_CLOCKWISE_VERTICAL("north clockwise vertical"), + NORTH_CLOCKWISE_BOTH("north clockwise both"), + NORTH_UPSIDE_DOWN_NONE("north upside down none"), + NORTH_UPSIDE_DOWN_HORIZONTAL("north upside down horizontal"), + NORTH_UPSIDE_DOWN_VERTICAL("north upside down vertical"), + NORTH_UPSIDE_DOWN_BOTH("north upside down both"), + NORTH_COUNTER_CLOCKWISE_NONE("north counter clockwise none"), + NORTH_COUNTER_CLOCKWISE_HORIZONTAL("north counter clockwise horizontal"), + NORTH_COUNTER_CLOCKWISE_VERTICAL("north counter clockwise vertical"), + NORTH_COUNTER_CLOCKWISE_BOTH("north counter clockwise both"), + SOUTH_NORMAL_NONE("south normal none"), + SOUTH_NORMAL_HORIZONTAL("south normal horizontal"), + SOUTH_NORMAL_VERTICAL("south normal vertical"), + SOUTH_NORMAL_BOTH("south normal both"), + SOUTH_CLOCKWISE_NONE("south clockwise none"), + SOUTH_CLOCKWISE_HORIZONTAL("south clockwise horizontal"), + SOUTH_CLOCKWISE_VERTICAL("south clockwise vertical"), + SOUTH_CLOCKWISE_BOTH("south clockwise both"), + SOUTH_UPSIDE_DOWN_NONE("south upside down none"), + SOUTH_UPSIDE_DOWN_HORIZONTAL("south upside down horizontal"), + SOUTH_UPSIDE_DOWN_VERTICAL("south upside down vertical"), + SOUTH_UPSIDE_DOWN_BOTH("south upside down both"), + SOUTH_COUNTER_CLOCKWISE_NONE("south counter clockwise none"), + SOUTH_COUNTER_CLOCKWISE_HORIZONTAL("south counter clockwise horizontal"), + SOUTH_COUNTER_CLOCKWISE_VERTICAL("south counter clockwise vertical"), + SOUTH_COUNTER_CLOCKWISE_BOTH("south counter clockwise both"), + WEST_NORMAL_NONE("west normal none"), + WEST_NORMAL_HORIZONTAL("west normal horizontal"), + WEST_NORMAL_VERTICAL("west normal vertical"), + WEST_NORMAL_BOTH("west normal both"), + WEST_CLOCKWISE_NONE("west clockwise none"), + WEST_CLOCKWISE_HORIZONTAL("west clockwise horizontal"), + WEST_CLOCKWISE_VERTICAL("west clockwise vertical"), + WEST_CLOCKWISE_BOTH("west clockwise both"), + WEST_UPSIDE_DOWN_NONE("west upside down none"), + WEST_UPSIDE_DOWN_HORIZONTAL("west upside down horizontal"), + WEST_UPSIDE_DOWN_VERTICAL("west upside down vertical"), + WEST_UPSIDE_DOWN_BOTH("west upside down both"), + WEST_COUNTER_CLOCKWISE_NONE("west counter clockwise none"), + WEST_COUNTER_CLOCKWISE_HORIZONTAL("west counter clockwise horizontal"), + WEST_COUNTER_CLOCKWISE_VERTICAL("west counter clockwise vertical"), + WEST_COUNTER_CLOCKWISE_BOTH("west counter clockwise both"), + EAST_NORMAL_NONE("east normal none"), + EAST_NORMAL_HORIZONTAL("east normal horizontal"), + EAST_NORMAL_VERTICAL("east normal vertical"), + EAST_NORMAL_BOTH("east normal both"), + EAST_CLOCKWISE_NONE("east clockwise none"), + EAST_CLOCKWISE_HORIZONTAL("east clockwise horizontal"), + EAST_CLOCKWISE_VERTICAL("east clockwise vertical"), + EAST_CLOCKWISE_BOTH("east clockwise both"), + EAST_UPSIDE_DOWN_NONE("east upside down none"), + EAST_UPSIDE_DOWN_HORIZONTAL("east upside down horizontal"), + EAST_UPSIDE_DOWN_VERTICAL("east upside down vertical"), + EAST_UPSIDE_DOWN_BOTH("east upside down both"), + EAST_COUNTER_CLOCKWISE_NONE("east counter clockwise none"), + EAST_COUNTER_CLOCKWISE_HORIZONTAL("east counter clockwise horizontal"), + EAST_COUNTER_CLOCKWISE_VERTICAL("east counter clockwise vertical"), + EAST_COUNTER_CLOCKWISE_BOTH("east counter clockwise both"); + + public static final ExtendedFacing DEFAULT=NORTH_NORMAL_NONE; + public static final ExtendedFacing[] VALUES = values(); + public static final Map> FOR_FACING=new HashMap<>(); + static { + stream(values()).forEach(extendedFacing -> + FOR_FACING.compute(extendedFacing.direction,((forgeDirection, extendedFacings) -> { + if(extendedFacings==null){ + extendedFacings = new ArrayList<>(); + } + extendedFacings.add(extendedFacing); + return extendedFacings; + }))); + } + private static final Map NAME_LOOKUP = stream(VALUES).collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing)); + + private final ForgeDirection direction; + private final ForgeDirection a,b,c; + private final Rotation rotation; + private final Flip flip; + + private final String name; + private final IntegerAxisSwap integerAxisSwap; + + ExtendedFacing(String name) { + this.name = name; + direction= Direction.VALUES[ordinal()/(ROTATIONS_COUNT*FLIPS_COUNT)].getForgeDirection(); + rotation=Rotation.VALUES[ordinal()/FLIPS_COUNT-direction.ordinal()*ROTATIONS_COUNT]; + flip=Flip.VALUES[ordinal()%FLIPS_COUNT]; + ForgeDirection a,b,c; + switch (direction){ + case DOWN: + a= ForgeDirection.WEST; + b= ForgeDirection.SOUTH; + c= ForgeDirection.UP; + break; + case UP: + a= ForgeDirection.EAST; + b= ForgeDirection.SOUTH; + c= ForgeDirection.DOWN; + break; + case NORTH: + a= ForgeDirection.WEST; + b= ForgeDirection.DOWN; + c= ForgeDirection.SOUTH; + break; + case SOUTH: + a= ForgeDirection.EAST; + b= ForgeDirection.DOWN; + c= ForgeDirection.NORTH; + break; + case WEST: + a= ForgeDirection.SOUTH; + b= ForgeDirection.DOWN; + c= ForgeDirection.EAST; + break; + case EAST: + a= ForgeDirection.NORTH; + b= ForgeDirection.DOWN; + c= ForgeDirection.WEST; + break; + default:throw new RuntimeException("Is impossible..."); + } + switch (flip){//This duplicates some axis swaps since flip boolean would do, but seems more convenient to use + case HORIZONTAL: + a=a.getOpposite(); + break; + case BOTH: + a=a.getOpposite(); + case VERTICAL: + b=b.getOpposite(); + break; + case NONE: break; + default:throw new RuntimeException("Even more impossible..."); + } + switch (rotation) { + case CLOCKWISE: { + ForgeDirection _a=a; + a =b; + b =_a.getOpposite(); + break; + } + case UPSIDE_DOWN: + a=a.getOpposite(); + b=b.getOpposite(); + break; + case COUNTER_CLOCKWISE: { + ForgeDirection _a=a; + a =b.getOpposite(); + b =_a; + break; + } + case NORMAL: break; + default: + throw new RuntimeException("More impossible..."); + } + this.a=a; + this.b=b; + this.c=c; + integerAxisSwap =new IntegerAxisSwap(a,b,c); + } + + public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip){ + if(direction==ForgeDirection.UNKNOWN){ + return VALUES[IAlignment.getAlignmentIndex(ForgeDirection.NORTH, rotation, flip)]; + } + return VALUES[IAlignment.getAlignmentIndex(direction, rotation, flip)]; + } + + public static ExtendedFacing of(ForgeDirection direction){ + if(direction==ForgeDirection.UNKNOWN){ + return DEFAULT; + } + return VALUES[IAlignment.getAlignmentIndex(direction, Rotation.NORMAL, Flip.NONE)]; + } + + public ExtendedFacing with(ForgeDirection direction){ + return of(direction,rotation,flip); + } + + public ExtendedFacing with(Rotation rotation){ + return of(direction,rotation,flip); + } + + public ExtendedFacing with(Flip flip){ + return of(direction,rotation,flip); + } + + public ExtendedFacing getOppositeDirection() { + return of(direction.getOpposite(),rotation,flip); + } + + public ExtendedFacing getOppositeRotation() { + return of(direction,rotation.getOpposite(),flip); + } + + public ExtendedFacing getOppositeFlip() { + return of(direction,rotation,flip.getOpposite()); + } + + /** + * Gets the same effective facing achieved by different rot/flip combo + * @return same effective facing, but different enum value + */ + public ExtendedFacing getDuplicate(){ + return of(direction,rotation.getOpposite(),flip.getOpposite()); + } + + public int getIndex(){ + return ordinal(); + } + + public String getName2() { + return this.name; + } + + public static ExtendedFacing byName(String name) { + return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT)); + } + + public static ExtendedFacing byIndex(int index) { + return VALUES[abs(index % VALUES.length)]; + } + + public static ExtendedFacing random(Random rand) { + return VALUES[rand.nextInt(VALUES.length)]; + } + + public ForgeDirection getDirection() { + return direction; + } + + public Rotation getRotation() { + return rotation; + } + + public Flip getFlip() { + return flip; + } + + /** + * Translates relative to front facing offset to world offset + * @param abcOffset A,B,C offset (facing relative L-->R,U-->D,F-->B) + * @return X,Y,Z offset in world + */ + public Vec3 getWorldOffset(Vec3 abcOffset) { + return integerAxisSwap.inverseTranslate(abcOffset); + } + public Vec3Impl getWorldOffset(Vec3Impl abcOffset) { + return integerAxisSwap.inverseTranslate(abcOffset); + } + public void getWorldOffset(int[] point,int[] out){ + integerAxisSwap.inverseTranslate(point,out); + } + public void getWorldOffset(double[] point,double[] out){ + integerAxisSwap.inverseTranslate(point,out); + } + + + /** + * Translates world offset to relative front facing offset + * @param xyzOffset X,Y,Z offset in world + * @return A,B,C offset (facing relative L-->R,U-->D,F-->B) + */ + public Vec3 getOffsetABC(Vec3 xyzOffset){ + return integerAxisSwap.translate(xyzOffset); + } + public Vec3Impl getOffsetABC(Vec3Impl xyzOffset){ + return integerAxisSwap.translate(xyzOffset); + } + public void getOffsetABC(int[] point,int[] out){ + integerAxisSwap.translate(point,out); + } + public void getOffsetABC(double[] point,double[] out){ + integerAxisSwap.translate(point,out); + } + + public IntegerAxisSwap getIntegerAxisSwap() { + return integerAxisSwap; + } + + public ForgeDirection getRelativeLeftInWorld() { + return a; + } + + public ForgeDirection getRelativeRightInWorld() { + return a.getOpposite(); + } + + public ForgeDirection getRelativeDownInWorld() { + return b; + } + + public ForgeDirection getRelativeUpInWorld() { + return b.getOpposite(); + } + + public ForgeDirection getRelativeBackInWorld() { + return c; + } + + public ForgeDirection getRelativeForwardInWorld() { + return c.getOpposite(); + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Flip.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Flip.java new file mode 100644 index 00000000..76dc5f5d --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Flip.java @@ -0,0 +1,76 @@ +package com.gtnewhorizon.structurelib.alignment.enumerable; + +import javax.annotation.Nonnull; +import java.util.Locale; +import java.util.Map; +import java.util.Random; + +import static java.lang.Math.abs; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toMap; + +public enum Flip { + NONE(3, "none"), + HORIZONTAL(2, "horizontal"), + VERTICAL(1, "vertical"), + BOTH(0, "both"); + + private final int opposite; + private final String name; + + public static final Flip[] VALUES = values(); + private static final Map NAME_LOOKUP = stream(VALUES).collect(toMap(Flip::getName2, (flip) -> flip)); + + Flip(int oppositeIn, String nameIn) { + this.opposite = oppositeIn; + this.name = nameIn; + } + + public int getIndex(){ + return ordinal(); + } + + public Flip getOpposite() { + return VALUES[opposite]; + } + + public String getName2() { + return this.name; + } + + public static Flip byName(String name) { + return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT)); + } + + public static Flip byIndex(int index) { + return VALUES[abs(index % VALUES.length)]; + } + + public static Flip random(@Nonnull Random rand) { + return VALUES[rand.nextInt(VALUES.length)]; + } + + public String toString() { + return this.name; + } + + public String getName() { + return this.name; + } + + public boolean isNotFlipped(){ + return this==NONE; + } + + public boolean isBothFlipped(){ + return this==BOTH; + } + + public boolean isHorizontallyFlipped() { + return this==HORIZONTAL || isBothFlipped(); + } + + public boolean isVerticallyFliped() { + return this==VERTICAL || isBothFlipped(); + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Rotation.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Rotation.java new file mode 100644 index 00000000..9ae01f20 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Rotation.java @@ -0,0 +1,76 @@ +package com.gtnewhorizon.structurelib.alignment.enumerable; + +import javax.annotation.Nonnull; +import java.util.Locale; +import java.util.Map; +import java.util.Random; + +import static java.lang.Math.abs; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toMap; + +public enum Rotation { + NORMAL(2, "normal"), + CLOCKWISE(3, "clockwise"), + UPSIDE_DOWN(0, "upside down"), + COUNTER_CLOCKWISE(1, "counter clockwise"); + + private final int opposite; + private final String name; + + public static final Rotation[] VALUES = values(); + private static final Map NAME_LOOKUP = stream(VALUES).collect(toMap(Rotation::getName2, (rotation) -> rotation)); + + Rotation(int oppositeIn, String nameIn) { + this.opposite = oppositeIn; + this.name = nameIn; + } + + public int getIndex(){ + return ordinal(); + } + + public Rotation getOpposite() { + return VALUES[opposite]; + } + + public String getName2() { + return this.name; + } + + public static Rotation byName(String name) { + return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT)); + } + + public static Rotation byIndex(int index) { + return VALUES[abs(index % VALUES.length)]; + } + + public static Rotation random(@Nonnull Random rand) { + return VALUES[rand.nextInt(VALUES.length)]; + } + + public String toString() { + return this.name; + } + + public String getName() { + return this.name; + } + + public boolean isNotRotated(){ + return this==NORMAL; + } + + public boolean isClockwise(){ + return this==CLOCKWISE; + } + + public boolean isCounterClockwise(){ + return this==COUNTER_CLOCKWISE; + } + + public boolean isUpsideDown(){ + return this==UPSIDE_DOWN; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/entity/fx/BlockHint.java b/src/main/java/com/gtnewhorizon/structurelib/entity/fx/BlockHint.java new file mode 100644 index 00000000..148e3156 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/entity/fx/BlockHint.java @@ -0,0 +1,133 @@ +package com.gtnewhorizon.structurelib.entity.fx; + +import com.gtnewhorizon.structurelib.StructureLib; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.block.Block; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.init.Blocks; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL11; + +@SideOnly(Side.CLIENT) +public class BlockHint extends EntityFX { + private IIcon[] icons; + private short[] mRGBa = {255,255,255,0}; + + public BlockHint(World world){ + this(world,0,0,0, Blocks.stone,0); + } + + /** + * + * @param world + * @param x + * @param y + * @param z + * @param icons DOWN, UP, NORTH, SOUTH, WEST, EAST + */ + public BlockHint(World world, int x, int y, int z, IIcon[] icons) { + super(world, x + .25, y + .5, z + .25); + particleGravity = 0; + prevPosX = posX; + prevPosY = posY; + prevPosZ = posZ; + noClip = true; + particleMaxAge = 2000 + StructureLib.RANDOM.nextInt(200); + this.icons=icons; + } + + public BlockHint(World world, int x, int y, int z, Block block, int meta) { + super(world, x+.25, y+.5, z+.25); + particleGravity = 0; + prevPosX = posX; + prevPosY = posY; + prevPosZ = posZ; + noClip = true; + particleMaxAge = 2000 + StructureLib.RANDOM.nextInt(200); + icons = new IIcon[6]; + for (int i = 0; i < 6; i++) { + icons[i] = block.getIcon(i, meta); + } + } + + public BlockHint withColorTint(short[] coloure){ + this.mRGBa =coloure; + return this; + } + + @Override + public void renderParticle(Tessellator tes, float subTickTime, float p_70539_3_, float p_70539_4_, float p_70539_5_, float p_70539_6_, float p_70539_7_) { + float size = .5f; + float X = (float) (prevPosX + (posX - prevPosX) * (double) subTickTime - EntityFX.interpPosX); + float Y = (float) (prevPosY + (posY - prevPosY) * (double) subTickTime - EntityFX.interpPosY) - size / 2; + float Z = (float) (prevPosZ + (posZ - prevPosZ) * (double) subTickTime - EntityFX.interpPosZ); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glDepthMask(false); + tes.setColorRGBA((int) (mRGBa[0] * .9F), (int) (mRGBa[1] * .95F), (int) (mRGBa[2] * 1F), 192); + + //var8, var9 - X U + //var 10, var 11 - Y V + for(int i=0;i<6;i++){ + if(icons[i]==null) { + continue; + } + double u=icons[i].getMinU(); + double U=icons[i].getMaxU(); + double v=icons[i].getMinV(); + double V=icons[i].getMaxV(); + switch (i){//{DOWN, UP, NORTH, SOUTH, WEST, EAST} + case 0: + tes.addVertexWithUV(X, Y, Z + size, u, V); + tes.addVertexWithUV(X, Y, Z, u, v); + tes.addVertexWithUV(X + size, Y, Z, U, v); + tes.addVertexWithUV(X + size, Y, Z + size, U, V); + break; + case 1: + tes.addVertexWithUV(X, Y + size, Z, u, v); + tes.addVertexWithUV(X, Y + size, Z + size, u, V); + tes.addVertexWithUV(X + size, Y + size, Z + size, U, V); + tes.addVertexWithUV(X + size, Y + size, Z, U, v); + break; + case 2: + tes.addVertexWithUV(X, Y, Z, U, V); + tes.addVertexWithUV(X, Y + size, Z, U, v); + tes.addVertexWithUV(X + size, Y + size, Z, u, v); + tes.addVertexWithUV(X + size, Y, Z, u, V); + break; + case 3: + tes.addVertexWithUV(X + size, Y, Z + size, U, V); + tes.addVertexWithUV(X + size, Y + size, Z + size, U, v); + tes.addVertexWithUV(X, Y + size, Z + size, u, v); + tes.addVertexWithUV(X, Y, Z + size, u, V); + break; + case 4: + tes.addVertexWithUV(X, Y, Z + size, U, V); + tes.addVertexWithUV(X, Y + size, Z + size, U, v); + tes.addVertexWithUV(X, Y + size, Z, u, v); + tes.addVertexWithUV(X, Y, Z, u, V); + break; + case 5: + tes.addVertexWithUV(X + size, Y, Z, U, V); + tes.addVertexWithUV(X + size, Y + size, Z, U, v); + tes.addVertexWithUV(X + size, Y + size, Z + size, u, v); + tes.addVertexWithUV(X + size, Y, Z + size, u, V); + break; + } + } + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDepthMask(true); + } + + @Override + public int getFXLayer() { + return 1; + } + + @Override + public boolean shouldRenderInPass(int pass) { + return pass==2; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/entity/fx/WeightlessParticleFX.java b/src/main/java/com/gtnewhorizon/structurelib/entity/fx/WeightlessParticleFX.java new file mode 100644 index 00000000..b2afa126 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/entity/fx/WeightlessParticleFX.java @@ -0,0 +1,44 @@ +package com.gtnewhorizon.structurelib.entity.fx; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class WeightlessParticleFX extends EntityFX +{ + public WeightlessParticleFX(World p_i1205_1_, double p_i1205_2_, double p_i1205_4_, double p_i1205_6_, double p_i1205_8_, double p_i1205_10_, double p_i1205_12_) + { + super(p_i1205_1_, p_i1205_2_, p_i1205_4_, p_i1205_6_, p_i1205_8_, p_i1205_10_, p_i1205_12_); + this.motionX = p_i1205_8_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F); + this.motionY = p_i1205_10_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F); + this.motionZ = p_i1205_12_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F); + this.particleRed = this.particleGreen = this.particleBlue = this.rand.nextFloat() * 0.3F + 0.7F; + this.particleScale = this.rand.nextFloat() * this.rand.nextFloat() * 6.0F + 1.0F; + this.particleMaxAge = (int)(16.0D / ((double)this.rand.nextFloat() * 0.8D + 0.2D)) + 2; + this.noClip=true; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + this.setParticleTextureIndex(7 - this.particleAge * 8 / this.particleMaxAge); + //this.motionY += 0.004D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.8999999761581421D; + this.motionY *= 0.8999999761581421D; + this.motionZ *= 0.8999999761581421D; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/proxy/ClientProxy.java b/src/main/java/com/gtnewhorizon/structurelib/proxy/ClientProxy.java new file mode 100644 index 00000000..eddc18a1 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/proxy/ClientProxy.java @@ -0,0 +1,43 @@ +package com.gtnewhorizon.structurelib.proxy; + +import com.gtnewhorizon.structurelib.entity.fx.BlockHint; +import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import static com.gtnewhorizon.structurelib.StructureLib.RANDOM; + +public class ClientProxy extends CommonProxy { + @Override + public void hint_particle_tinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) { + Minecraft.getMinecraft().effectRenderer.addEffect(new BlockHint(w, x, y, z, icons).withColorTint(RGBa)); + createParticleFX(w, x, y, z); + } + + @Override + public void hint_particle_tinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) { + Minecraft.getMinecraft().effectRenderer.addEffect(new BlockHint(w, x, y, z, block, meta).withColorTint(RGBa)); + createParticleFX(w, x, y, z); + } + + @Override + public void hint_particle(World w, int x, int y, int z, IIcon[] icons) { + Minecraft.getMinecraft().effectRenderer.addEffect(new BlockHint(w, x, y, z, icons)); + createParticleFX(w, x, y, z); + } + + @Override + public void hint_particle(World w, int x, int y, int z, Block block, int meta) { + Minecraft.getMinecraft().effectRenderer.addEffect(new BlockHint(w, x, y, z, block, meta)); + createParticleFX(w, x, y, z); + } + + private static void createParticleFX(World w, int x, int y, int z) { + EntityFX particle = new WeightlessParticleFX(w, x + RANDOM.nextFloat() * 0.5F, y + RANDOM.nextFloat() * 0.5F, z + RANDOM.nextFloat() * 0.5F, 0, 0, 0); + particle.setRBGColorF(0, 0.6F * RANDOM.nextFloat(), 0.8f); + Minecraft.getMinecraft().effectRenderer.addEffect(particle); + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/proxy/CommonProxy.java b/src/main/java/com/gtnewhorizon/structurelib/proxy/CommonProxy.java new file mode 100644 index 00000000..1b51107f --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/proxy/CommonProxy.java @@ -0,0 +1,12 @@ +package com.gtnewhorizon.structurelib.proxy; + +import net.minecraft.block.Block; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +public class CommonProxy { + public void hint_particle_tinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa){} + public void hint_particle_tinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa){} + public void hint_particle(World w,int x, int y, int z, IIcon[] icons){} + public void hint_particle(World w,int x, int y, int z, Block block, int meta){} +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IBlockPosConsumer.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IBlockPosConsumer.java new file mode 100644 index 00000000..f875c54a --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IBlockPosConsumer.java @@ -0,0 +1,7 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.world.World; + +public interface IBlockPosConsumer { + void consume(World world, int x, int y, int z); +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/ICustomBlockSetting.java b/src/main/java/com/gtnewhorizon/structurelib/structure/ICustomBlockSetting.java new file mode 100644 index 00000000..5e6c0c81 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/ICustomBlockSetting.java @@ -0,0 +1,18 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.block.Block; +import net.minecraft.world.World; + +public interface ICustomBlockSetting { + /** + * Default block setting calls {@link World#setBlock(int x, int y, int z, Block block, int meta, int updateType)} like: + * {@code world.setBlock(x,y,z,this/block,meta,2)} where updateType 2 means to update lighting and stuff + * + * @param world world that should be affected + * @param x x position to set + * @param y y position to set + * @param z z position to set + * @param meta required meta + */ + void setBlock(World world, int x, int y, int z, int meta); +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java new file mode 100644 index 00000000..ad0286fc --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java @@ -0,0 +1,168 @@ +package com.gtnewhorizon.structurelib.structure; + +import com.gtnewhorizon.structurelib.StructureLib; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import java.util.Arrays; + +import static com.gtnewhorizon.structurelib.StructureLib.DEBUG_MODE; + +public interface IStructureDefinition { + /** + * Used internally + * + * @param name same name as for other methods here + * @return the array of elements to process + */ + IStructureElement[] getStructureFor(String name); + + default boolean check(T object, String piece, World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + boolean forceCheckAllBlocks) { + return iterate(object, null, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, false, forceCheckAllBlocks); + } + + default boolean hints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC) { + return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, true, null); + } + + default boolean build(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC) { + return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, false, null); + } + + default boolean buildOrHints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + boolean hintsOnly) { + return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, hintsOnly, null); + } + + static boolean iterate(T object, ItemStack trigger, IStructureElement[] elements, World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) { + if (world.isRemote ^ hintsOnly) { + return false; + } + + //change base position to base offset + basePositionA = -basePositionA; + basePositionB = -basePositionB; + basePositionC = -basePositionC; + + int[] abc = new int[]{basePositionA, basePositionB, basePositionC}; + int[] xyz = new int[3]; + + if (checkBlocksIfNotNullForceCheckAllIfTrue != null) { + if (checkBlocksIfNotNullForceCheckAllIfTrue) { + for (IStructureElement element : elements) { + if (element.isNavigating()) { + abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA(); + abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB(); + abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC(); + } else { + extendedFacing.getWorldOffset(abc, xyz); + xyz[0] += basePositionX; + xyz[1] += basePositionY; + xyz[2] += basePositionZ; + + if (world.blockExists(xyz[0], xyz[1], xyz[2])) { + if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) { + if (DEBUG_MODE) { + StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " + + Arrays.toString(xyz) + " " + Arrays.toString(abc)); + } + return false; + } + } else { + if (DEBUG_MODE) { + StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " + + Arrays.toString(xyz) + " " + Arrays.toString(abc)); + } + return false; + } + abc[0] += 1; + } + } + } else { + for (IStructureElement element : elements) { + if (element.isNavigating()) { + abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA(); + abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB(); + abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC(); + } else { + extendedFacing.getWorldOffset(abc, xyz); + xyz[0] += basePositionX; + xyz[1] += basePositionY; + xyz[2] += basePositionZ; + + if (world.blockExists(xyz[0], xyz[1], xyz[2])) { + if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) { + if (DEBUG_MODE) { + StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " + + Arrays.toString(xyz) + " " + Arrays.toString(abc)); + } + return false; + } + } else { + if (DEBUG_MODE) { + StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " + + Arrays.toString(xyz) + " " + Arrays.toString(abc)); + } + } + abc[0] += 1; + } + } + } + } else { + if (hintsOnly) { + for (IStructureElement element : elements) { + if (element.isNavigating()) { + abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA(); + abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB(); + abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC(); + } else { + extendedFacing.getWorldOffset(abc, xyz); + xyz[0] += basePositionX; + xyz[1] += basePositionY; + xyz[2] += basePositionZ; + + element.spawnHint(object, world, xyz[0], xyz[1], xyz[2], trigger); + + abc[0] += 1; + } + } + } else { + for (IStructureElement element : elements) { + if (element.isNavigating()) { + abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA(); + abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB(); + abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC(); + } else { + extendedFacing.getWorldOffset(abc, xyz); + xyz[0] += basePositionX; + xyz[1] += basePositionY; + xyz[2] += basePositionZ; + + if (world.blockExists(xyz[0], xyz[1], xyz[2])) { + element.placeBlock(object, world, xyz[0], xyz[1], xyz[2], trigger); + } + abc[0] += 1; + } + } + } + } + return true; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java new file mode 100644 index 00000000..4334c16b --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java @@ -0,0 +1,43 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * Use StructureUtility to instantiate + */ +public interface IStructureElement { + boolean check(T t, World world, int x, int y, int z); + + boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger); + + boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger); + + default int getStepA() { + return 1; + } + + default int getStepB() { + return 0; + } + + default int getStepC() { + return 0; + } + + default boolean resetA() { + return false; + } + + default boolean resetB() { + return false; + } + + default boolean resetC() { + return false; + } + + default boolean isNavigating() { + return false; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java new file mode 100644 index 00000000..31b0e9e2 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java @@ -0,0 +1,41 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * Use StructureUtility to instantiate + */ +public interface IStructureElementChain extends IStructureElement { + IStructureElement[] fallbacks(); + + @Override + default boolean check(T t, World world, int x, int y, int z) { + for (IStructureElement fallback : fallbacks()) { + if (fallback.check(t, world, x, y, z)) { + return true; + } + } + return false; + } + + @Override + default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + for (IStructureElement fallback : fallbacks()) { + if (fallback.spawnHint(t, world, x, y, z, trigger)) { + return true; + } + } + return false; + } + + @Override + default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + for (IStructureElement fallback : fallbacks()) { + if (fallback.placeBlock(t, world, x, y, z, trigger)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java new file mode 100644 index 00000000..3774a2f0 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java @@ -0,0 +1,16 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public interface IStructureElementCheckOnly extends IStructureElement { + @Override + default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return false; + } + + @Override + default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return false; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementDeferred.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementDeferred.java new file mode 100644 index 00000000..cf5ab2d9 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementDeferred.java @@ -0,0 +1,7 @@ +package com.gtnewhorizon.structurelib.structure; + +/** + * Use StructureUtility to instantiate + */ +public interface IStructureElementDeferred extends IStructureElement { +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java new file mode 100644 index 00000000..90259b5e --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java @@ -0,0 +1,11 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public interface IStructureElementNoPlacement extends IStructureElement { + @Override + default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return false; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java new file mode 100644 index 00000000..707cb197 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java @@ -0,0 +1,28 @@ +package com.gtnewhorizon.structurelib.structure; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * Use StructureUtility to instantiate + */ +interface IStructureNavigate extends IStructureElement { + @Override + default boolean check(T t, World world, int x, int y, int z) { + return true; + } + + @Override + default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + + @Override + default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + + default boolean isNavigating() { + return true; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java new file mode 100644 index 00000000..81b33e14 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java @@ -0,0 +1,211 @@ +package com.gtnewhorizon.structurelib.structure; + +import com.gtnewhorizon.structurelib.util.Vec3Impl; + +import java.util.*; + +import static com.gtnewhorizon.structurelib.StructureLib.DEBUG_MODE; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.*; + +public class StructureDefinition implements IStructureDefinition { + private final Map> elements; + private final Map shapes; + private final Map[]> structures; + + public static Builder builder() { + return new Builder<>(); + } + + private StructureDefinition( + Map> elements, + Map shapes, + Map[]> structures) { + this.elements = elements; + this.shapes = shapes; + this.structures = structures; + } + + public static class Builder { + private static final char A = '\uA000'; + private static final char B = '\uB000'; + private static final char C = '\uC000'; + private char d = '\uD000'; + private final Map navigates; + private final Map> elements; + private final Map shapes; + + private Builder() { + navigates = new HashMap<>(); + elements = new HashMap<>(); + shapes = new HashMap<>(); + } + + public Map> getElements() { + return elements; + } + + public Map getShapes() { + return shapes; + } + + /** + * Adds shape + * +- is air/no air checks + * space bar is skip + * ~ is also skip (but marks controller position, optional and logically it is a space...) + * rest needs to be defined + *

+ * next char is next block(a) + * next string is next line(b) + * next string[] is next slice(c) + *

+ * char A000-FFFF range is reserved for generated skips + * + * @param name unlocalized/code name + * @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null afterwards + * @return this builder + */ + public Builder addShape(String name, String[][] structurePiece) { + StringBuilder builder = new StringBuilder(); + if (structurePiece.length > 0) { + for (String[] strings : structurePiece) { + if (strings.length > 0) { + for (String string : strings) { + builder.append(string).append(B); + } + builder.setLength(builder.length() - 1); + } + builder.append(C); + } + builder.setLength(builder.length() - 1); + } + int a = 0, b = 0, c = 0; + for (int i = 0; i < builder.length(); i++) { + char ch = builder.charAt(i); + if (ch == ' ' || ch == '~') { + builder.setCharAt(i, A); + ch = A; + } + if (ch == A) { + a++; + } else if (ch == B) { + a = 0; + b++; + } else if (ch == C) { + a = 0; + b = 0; + c++; + } else if (a != 0 || b != 0 || c != 0) { + Vec3Impl vec3 = new Vec3Impl(a, b, c); + Character navigate = navigates.get(vec3); + if (navigate == null) { + navigate = d++; + navigates.put(vec3, navigate); + addElement(navigate, step(vec3)); + } + builder.setCharAt(i - 1, navigate); + a = 0; + b = 0; + c = 0; + } + } + + String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", ""); + + if (built.contains("+")) { + addElement('+', notAir()); + } + if (built.contains("-")) { + addElement('-', isAir()); + } + shapes.put(name, built); + return this; + } + + public Builder addElement(Character name, IStructureElement structurePiece) { + elements.putIfAbsent(name, structurePiece); + return this; + } + + public IStructureDefinition build() { + Map[]> structures = compileStructureMap(); + if (DEBUG_MODE) { + return new StructureDefinition<>(new HashMap<>(elements), new HashMap<>(shapes), structures); + } else { + return structures::get; + } + } + + @SuppressWarnings("unchecked") + private Map[]> compileElementSetMap() { + Set missing = new HashSet<>(); + shapes.values().stream().map(CharSequence::chars).forEach(intStream -> intStream.forEach(c -> { + IStructureElement iStructureElement = elements.get((char) c); + if (iStructureElement == null) { + missing.add(c); + } + })); + if (missing.isEmpty()) { + Map[]> map = new HashMap<>(); + shapes.forEach((key, value) -> { + Set chars = new HashSet<>(); + for (char c : value.toCharArray()) { + chars.add(c); + } + IStructureElement[] compiled = new IStructureElement[chars.size()]; + int i = 0; + for (Character aChar : chars) { + compiled[i++] = elements.get(aChar); + } + map.put(key, compiled); + }); + return map; + } else { + throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " + + Arrays.toString(missing.toArray())); + } + } + + @SuppressWarnings("unchecked") + private Map[]> compileStructureMap() { + Set mising = new HashSet<>(); + shapes.values().stream().map(CharSequence::chars).forEach(intStream -> intStream.forEach(c -> { + IStructureElement iStructureElement = elements.get((char) c); + if (iStructureElement == null) { + mising.add(c); + } + })); + if (mising.isEmpty()) { + Map[]> map = new HashMap<>(); + shapes.forEach((key, value) -> { + IStructureElement[] compiled = new IStructureElement[value.length()]; + for (int i = 0; i < value.length(); i++) { + compiled[i] = elements.get(value.charAt(i)); + } + map.put(key, compiled); + }); + return map; + } else { + throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " + + Arrays.toString(mising.toArray())); + } + } + } + + public Map> getElements() { + return elements; + } + + public Map getShapes() { + return shapes; + } + + public Map[]> getStructures() { + return structures; + } + + @Override + public IStructureElement[] getStructureFor(String name) { + return structures.get(name); + } +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java new file mode 100644 index 00000000..1a656943 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java @@ -0,0 +1,1274 @@ +package com.gtnewhorizon.structurelib.structure; + +import com.gtnewhorizon.structurelib.StructureLib; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder; +import com.gtnewhorizon.structurelib.structure.adders.ITileAdder; +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import static java.lang.Integer.MIN_VALUE; + +/** + * Fluent API for structure checking! + *

+ * (Just import static this class to have a nice fluent syntax while defining structure definitions) + */ +public class StructureUtility { + private static final String NICE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=|!@#$%&()[]{};:<>/?_,.*^'`"; + // TODO make it an actual block + private static final Block sHintCasingsTT = Blocks.air; + @SuppressWarnings("rawtypes") + private static final Map STEP = new HashMap<>(); + @SuppressWarnings("rawtypes") + private static final IStructureElement AIR = new IStructureElement() { + @Override + public boolean check(Object t, World world, int x, int y, int z) { + return world.isAirBlock(x, y, z); + } + + @Override + public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, sHintCasingsTT, 13); + return true; + } + + @Override + public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, Blocks.air, 0, 2); + return false; + } + }; + @SuppressWarnings("rawtypes") + private static final IStructureElement NOT_AIR = new IStructureElement() { + @Override + public boolean check(Object t, World world, int x, int y, int z) { + return !world.isAirBlock(x, y, z); + } + + @Override + public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, sHintCasingsTT, 14); + return true; + } + + @Override + public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, sHintCasingsTT, 14, 2); + return true; + } + }; + @SuppressWarnings("rawtypes") + private static final IStructureElement ERROR = new IStructureElement() { + @Override + public boolean check(Object t, World world, int x, int y, int z) { + return false; + } + + @Override + public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, sHintCasingsTT, 15); + return true; + } + + @Override + public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + }; + + private StructureUtility() { + + } + + @SuppressWarnings("unchecked") + public static IStructureElement isAir() { + return AIR; + } + + @SuppressWarnings("unchecked") + public static IStructureElement notAir() { + return NOT_AIR; + } + + /** + * Check returns false. + * Placement is always handled by this and does nothing. + * Makes little to no use it in fallback chain. + * + */ + @SuppressWarnings("unchecked") + public static IStructureElement error() { + return ERROR; + } + + //region hint only + + /** + * Check always returns: true. + * + */ + public static IStructureElementNoPlacement ofHint(int dots) { + int meta = dots - 1; + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, sHintCasingsTT, meta); + return false; + } + }; + } + + /** + * Check always returns: true. + * + */ + public static IStructureElementNoPlacement ofHintDeferred(Supplier icons) { + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, icons.get()); + return false; + } + }; + } + + /** + * Check always returns: true. + * + */ + public static IStructureElementNoPlacement ofHintDeferred(Supplier icons, short[] RGBa) { + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle_tinted(world, x, y, z, icons.get(), RGBa); + return false; + } + }; + } + + //endregion + + //region block + + /** + * Does not allow Block duplicates (with different meta) + */ + public static IStructureElementNoPlacement ofBlocksFlatHint(Map blocsMap, Block hintBlock, int hintMeta) { + if (blocsMap == null || blocsMap.isEmpty() || hintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, hintBlock, hintMeta); + return true; + } + }; + } + + /** + * Allows block duplicates (with different meta) + */ + public static IStructureElementNoPlacement ofBlocksMapHint(Map> blocsMap, Block hintBlock, int hintMeta) { + if (blocsMap == null || blocsMap.isEmpty() || hintBlock == null) { + throw new IllegalArgumentException(); + } + for (Collection value : blocsMap.values()) { + if (value.isEmpty()) { + throw new IllegalArgumentException(); + } + } + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, hintBlock, hintMeta); + return true; + } + }; + } + + public static IStructureElementNoPlacement ofBlockHint(Block block, int meta, Block hintBlock, int hintMeta) { + if (block == null || hintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, hintBlock, hintMeta); + return true; + } + }; + } + + public static IStructureElementNoPlacement ofBlockHint(Block block, int meta) { + return ofBlockHint(block, meta, block, meta); + } + + public static IStructureElementNoPlacement ofBlockAdderHint(IBlockAdder iBlockAdder, Block hintBlock, int hintMeta) { + if (iBlockAdder == null || hintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, hintBlock, hintMeta); + return true; + } + }; + } + + /** + * Does not allow Block duplicates (with different meta) + */ + public static IStructureElement ofBlocksFlat(Map blocsMap, Block defaultBlock, int defaultMeta) { + if (blocsMap == null || blocsMap.isEmpty() || defaultBlock == null) { + throw new IllegalArgumentException(); + } + if (defaultBlock instanceof ICustomBlockSetting) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } else { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } + } + + /** + * Allows block duplicates (with different meta) + */ + public static IStructureElement ofBlocksMap(Map> blocsMap, Block defaultBlock, int defaultMeta) { + if (blocsMap == null || blocsMap.isEmpty() || defaultBlock == null) { + throw new IllegalArgumentException(); + } + for (Collection value : blocsMap.values()) { + if (value.isEmpty()) { + throw new IllegalArgumentException(); + } + } + if (defaultBlock instanceof ICustomBlockSetting) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } else { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } + } + + public static IStructureElement ofBlock(Block block, int meta, Block defaultBlock, int defaultMeta) { + if (block == null || defaultBlock == null) { + throw new IllegalArgumentException(); + } + if (block instanceof ICustomBlockSetting) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } else { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } + } + + /** + * Same as above but ignores target meta id + */ + public static IStructureElement ofBlockAnyMeta(Block block, Block defaultBlock, int defaultMeta) { + if (block == null || defaultBlock == null) { + throw new IllegalArgumentException(); + } + if (block instanceof ICustomBlockSetting) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return block == world.getBlock(x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } else { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return block == world.getBlock(x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } + } + + public static IStructureElement ofBlock(Block block, int meta) { + return ofBlock(block, meta, block, meta); + } + + /** + * Same as above but ignores target meta id + */ + public static IStructureElement ofBlockAnyMeta(Block block) { + return ofBlockAnyMeta(block, block, 0); + } + + /** + * Same as above but allows to set hint particle render + */ + public static IStructureElement ofBlockAnyMeta(Block block, int defaultMeta) { + return ofBlockAnyMeta(block, block, defaultMeta); + } + + //endregion + + //region adders + + public static IStructureElement ofBlockAdder(IBlockAdder iBlockAdder, Block defaultBlock, int defaultMeta) { + if (iBlockAdder == null || defaultBlock == null) { + throw new IllegalArgumentException(); + } + if (defaultBlock instanceof ICustomBlockSetting) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } else { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); + return true; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, defaultBlock, defaultMeta); + return true; + } + }; + } + } + + public static IStructureElement ofBlockAdder(IBlockAdder iBlockAdder, int dots) { + return ofBlockAdder(iBlockAdder, sHintCasingsTT, dots - 1); + } + + public static IStructureElementNoPlacement ofTileAdder(ITileAdder iTileAdder, Block hintBlock, int hintMeta) { + if (iTileAdder == null || hintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementNoPlacement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + // This used to check if it's a GT tile. Since this is now an standalone mod we no longer do this + return iTileAdder.apply(t, tileEntity); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLib.proxy.hint_particle(world, x, y, z, hintBlock, hintMeta); + return true; + } + }; + } + // No more hatch adder. Implement it via tile adder. We could of course add a wrapper around it in gregtech, but not any more in this standalone mod. + + //endregion + + //region side effects + + public static , T> IStructureElement onElementPass(Consumer onCheckPass, B element) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + boolean check = element.check(t, world, x, y, z); + if (check) { + onCheckPass.accept(t); + } + return check; + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static , T> IStructureElement onElementFail(Consumer onFail, B element) { + return new IStructureElement() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + boolean check = element.check(t, world, x, y, z); + if (!check) { + onFail.accept(t); + } + return check; + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.spawnHint(t, world, x, y, z, trigger); + } + }; + } + + //endregion + + /** + * Take care while chaining, as it will try to call every structure element until it returns true. + * If none does it will finally return false. + * + */ + @SafeVarargs + public static IStructureElementChain ofChain(IStructureElement... elementChain) { + if (elementChain == null || elementChain.length == 0) { + throw new IllegalArgumentException(); + } + for (IStructureElement iStructureElement : elementChain) { + if (iStructureElement == null) { + throw new IllegalArgumentException(); + } + } + return () -> elementChain; + } + + /** + * Take care while chaining, as it will try to call every structure element until it returns true. + * If none does it will finally return false. + * + */ + @SuppressWarnings("unchecked") + public static IStructureElementChain ofChain(List> elementChain) { + return ofChain(elementChain.toArray(new IStructureElement[0])); + } + + //region defer + + public static IStructureElementDeferred defer(Supplier> to) { + if (to == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return to.get().check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.get().placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.get().spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(Function> to) { + if (to == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return to.apply(t).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(Function keyExtractor, Map> map) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.get(keyExtractor.apply(t)).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t)).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t)).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(Function keyExtractor, Map> map, IStructureElement defaultElem) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.getOrDefault(keyExtractor.apply(t), defaultElem).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t), defaultElem).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t), defaultElem).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SafeVarargs + public static IStructureElementDeferred defer(Function keyExtractor, IStructureElement... array) { + if (keyExtractor == null || array == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return array[keyExtractor.apply(t)].check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t)].placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t)].spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SuppressWarnings("unchecked") + public static IStructureElementDeferred defer(Function keyExtractor, List> array) { + return defer(keyExtractor, array.toArray(new IStructureElement[0])); + } + + public static IStructureElementDeferred defer(BiFunction> to) { + if (to == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return to.apply(t, null).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(BiFunction keyExtractor, Map> map) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.get(keyExtractor.apply(t, null)).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t, trigger)).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t, trigger)).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(BiFunction keyExtractor, Map> map, IStructureElement defaultElem) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.getOrDefault(keyExtractor.apply(t, null), defaultElem).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SafeVarargs + public static IStructureElementDeferred defer(BiFunction keyExtractor, IStructureElement... array) { + if (keyExtractor == null || array == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return array[keyExtractor.apply(t, null)].check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t, trigger)].placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t, trigger)].spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SuppressWarnings("unchecked") + public static IStructureElementDeferred defer(BiFunction keyExtractor, List> array) { + return defer(keyExtractor, array.toArray(new IStructureElement[0])); + } + + public static IStructureElementDeferred defer(Function> toCheck, BiFunction> to) { + if (to == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return toCheck.apply(t).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, Map> map) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.get(keyExtractorCheck.apply(t)).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t, trigger)).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.get(keyExtractor.apply(t, trigger)).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, Map> map, IStructureElement defaultElem) { + if (keyExtractor == null || map == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return map.getOrDefault(keyExtractorCheck.apply(t), defaultElem).check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SafeVarargs + public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, IStructureElement... array) { + if (keyExtractor == null || array == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementDeferred() { + @Override + public boolean check(T t, World world, int x, int y, int z) { + return array[keyExtractorCheck.apply(t)].check(t, world, x, y, z); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t, trigger)].placeBlock(t, world, x, y, z, trigger); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + return array[keyExtractor.apply(t, trigger)].spawnHint(t, world, x, y, z, trigger); + } + }; + } + + @SuppressWarnings("unchecked") + public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, List> array) { + return defer(keyExtractorCheck, keyExtractor, array.toArray(new IStructureElement[0])); + } + + //endregion + + /** + * Used internally, to generate skips for structure definitions + * + */ + public static IStructureNavigate step(int a, int b, int c) { + return step(new Vec3Impl(a, b, c)); + } + + /** + * Used internally, to generate skips for structure definitions + * + */ + @SuppressWarnings("unchecked") + public static IStructureNavigate step(Vec3Impl step) { + if (step == null || step.get0() < 0 || step.get1() < 0 || step.get2() < 0) { + throw new IllegalArgumentException(); + } + return STEP.computeIfAbsent(step, vec3 -> { + if (vec3.get2() > 0) { + return stepC(vec3.get0(), vec3.get1(), vec3.get2()); + } else if (vec3.get1() > 0) { + return stepB(vec3.get0(), vec3.get1(), vec3.get2()); + } else { + return stepA(vec3.get0(), vec3.get1(), vec3.get2()); + } + }); + } + + private static IStructureNavigate stepA(int a, int b, int c) { + return new IStructureNavigate() { + @Override + public int getStepA() { + return a; + } + + @Override + public int getStepB() { + return b; + } + + @Override + public int getStepC() { + return c; + } + }; + } + + private static IStructureNavigate stepB(int a, int b, int c) { + return new IStructureNavigate() { + @Override + public int getStepA() { + return a; + } + + @Override + public int getStepB() { + return b; + } + + @Override + public int getStepC() { + return c; + } + + @Override + public boolean resetA() { + return true; + } + }; + } + + private static IStructureNavigate stepC(int a, int b, int c) { + return new IStructureNavigate() { + @Override + public int getStepA() { + return a; + } + + @Override + public int getStepB() { + return b; + } + + @Override + public int getStepC() { + return c; + } + + @Override + public boolean resetA() { + return true; + } + + @Override + public boolean resetB() { + return true; + } + }; + } + + /** + * Used only to get pseudo code in structure writer... + * + * NOTE: GT specific code got removed. TODO add a mean + * @param tileEntityClassifier return a string that denote the type of a tile entity, or null if it's nothing special. useful if the tile entity cannot be simply distinguished via getClass. + */ + public static String getPseudoJavaCode(World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + Function tileEntityClassifier, + int sizeA, int sizeB, int sizeC, boolean transpose) { + Map> blocks = new TreeMap<>(Comparator.comparing(Block::getUnlocalizedName)); + Set> tiles = new HashSet<>(); + Set specialTiles = new HashSet<>(); + iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, + sizeA, sizeB, sizeC, ((w, x, y, z) -> { + TileEntity tileEntity = w.getTileEntity(x, y, z); + if (tileEntity == null) { + Block block = w.getBlock(x, y, z); + if (block != null && block != Blocks.air) { + blocks.compute(block, (b, set) -> { + if (set == null) { + set = new TreeSet<>(); + } + set.add(block.getDamageValue(world, x, y, z)); + return set; + }); + } + } else { + String classification = tileEntityClassifier.apply(tileEntity); + if (classification == null) { + tiles.add(tileEntity.getClass()); + } else + specialTiles.add(classification); + } + })); + Map map = new HashMap<>(); + StringBuilder builder = new StringBuilder(); + { + int i = 0; + char c; + builder.append("\n\nStructure:\n") + .append("\nBlocks:\n"); + for (Map.Entry> entry : blocks.entrySet()) { + Block block = entry.getKey(); + Set set = entry.getValue(); + for (Integer meta : set) { + c = NICE_CHARS.charAt(i++); + if (i > NICE_CHARS.length()) { + return "Too complicated for nice chars"; + } + map.put(block.getUnlocalizedName() + '\0' + meta, c); + builder.append(c).append(" -> ofBlock...(") + .append(block.getUnlocalizedName()).append(", ").append(meta).append(", ...);\n"); + } + } + builder.append("\nTiles:\n"); + for (Class tile : tiles) { + c = NICE_CHARS.charAt(i++); + if (i > NICE_CHARS.length()) { + return "Too complicated for nice chars"; + } + map.put(tile.getCanonicalName(), c); + builder.append(c).append(" -> ofTileAdder(") + .append(tile).append(", ...);\n"); + } + builder.append("\nSpecial Tiles:\n"); + for (String tile : specialTiles) { + c = NICE_CHARS.charAt(i++); + if (i > NICE_CHARS.length()) { + return "Too complicated for nice chars"; + } + map.put(tile, c); + builder.append(c).append(" -> ofSpecialTileAdder(") + .append(tile).append(", ...); // You will probably want to change it to something else\n"); + } + } + builder.append("\nOffsets:\n") + .append(basePositionA).append(' ').append(basePositionB).append(' ').append(basePositionC).append('\n'); + if (transpose) { + builder.append("\nTransposed Scan:\n") + .append("new String[][]{\n") + .append(" {\""); + iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, true, + sizeA, sizeB, sizeC, ((w, x, y, z) -> { + TileEntity tileEntity = w.getTileEntity(x, y, z); + if (tileEntity == null) { + Block block = w.getBlock(x, y, z); + if (block != null && block != Blocks.air) { + builder.append(map.get(block.getUnlocalizedName() + '\0' + block.getDamageValue(world, x, y, z))); + } else { + builder.append(' '); + } + } else { + String classification = tileEntityClassifier.apply(tileEntity); + if (classification == null) { + classification = tileEntity.getClass().getCanonicalName(); + } + builder.append(map.get(classification)); + } + }), + () -> builder.append("\",\""), + () -> { + builder.setLength(builder.length() - 2); + builder.append("},\n {\""); + }); + builder.setLength(builder.length() - 8); + builder.append("\n}\n\n"); + } else { + builder.append("\nNormal Scan:\n") + .append("new String[][]{{\n") + .append(" \""); + iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ, + basePositionA, basePositionB, basePositionC, false, + sizeA, sizeB, sizeC, ((w, x, y, z) -> { + TileEntity tileEntity = w.getTileEntity(x, y, z); + if (tileEntity == null) { + Block block = w.getBlock(x, y, z); + if (block != null && block != Blocks.air) { + builder.append(map.get(block.getUnlocalizedName() + '\0' + block.getDamageValue(world, x, y, z))); + } else { + builder.append(' '); + } + } else { + String classification = tileEntityClassifier.apply(tileEntity); + if (classification == null) { + classification = tileEntity.getClass().getCanonicalName(); + } + builder.append(map.get(classification)); + } + }), + () -> builder.append("\",\n").append(" \""), + () -> { + builder.setLength(builder.length() - 7); + builder.append("\n").append("},{\n").append(" \""); + }); + builder.setLength(builder.length() - 8); + builder.append("}\n\n"); + } + return (builder.toString().replaceAll("\"\"", "E")); + } + + public static void iterate(World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + int sizeA, int sizeB, int sizeC, + IBlockPosConsumer iBlockPosConsumer) { + sizeA -= basePositionA; + sizeB -= basePositionB; + sizeC -= basePositionC; + + int[] abc = new int[3]; + int[] xyz = new int[3]; + + for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) { + for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) { + for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) { + extendedFacing.getWorldOffset(abc, xyz); + iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ); + } + + } + } + } + + public static void iterate(World world, ExtendedFacing extendedFacing, + int basePositionX, int basePositionY, int basePositionZ, + int basePositionA, int basePositionB, int basePositionC, + boolean transpose, int sizeA, int sizeB, int sizeC, + IBlockPosConsumer iBlockPosConsumer, + Runnable nextB, + Runnable nextC) { + sizeA -= basePositionA; + sizeB -= basePositionB; + sizeC -= basePositionC; + + int[] abc = new int[3]; + int[] xyz = new int[3]; + if (transpose) { + for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) { + for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) { + for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) { + extendedFacing.getWorldOffset(abc, xyz); + iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ); + } + nextB.run(); + } + nextC.run(); + } + } else { + for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) { + for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) { + for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) { + extendedFacing.getWorldOffset(abc, xyz); + iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ); + } + nextB.run(); + } + nextC.run(); + } + } + } + + /** + * Transposes shape (swaps B and C axis, can be used to un-transpose transposed shape) + * WARNING! Do not use on old api... + * + * @param structurePiece shape (transposed shape) + * @return transposed shape (untransposed shape) + */ + public static String[][] transpose(String[][] structurePiece) { + String[][] shape = new String[structurePiece[0].length][structurePiece.length]; + for (int i = 0; i < structurePiece.length; i++) { + for (int j = 0; j < structurePiece[i].length; j++) { + shape[j][i] = structurePiece[i][j]; + } + } + return shape; + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/adders/IBlockAdder.java b/src/main/java/com/gtnewhorizon/structurelib/structure/adders/IBlockAdder.java new file mode 100644 index 00000000..a530096b --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/adders/IBlockAdder.java @@ -0,0 +1,15 @@ +package com.gtnewhorizon.structurelib.structure.adders; + + +import net.minecraft.block.Block; + +public interface IBlockAdder { + /** + * Callback on block added, needs to check if block is valid (and add it) + * + * @param block block attempted to add + * @param meta meta of block attempted to add + * @return is structure still valid + */ + boolean apply(T t, Block block, Integer meta); +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/adders/ITileAdder.java b/src/main/java/com/gtnewhorizon/structurelib/structure/adders/ITileAdder.java new file mode 100644 index 00000000..1445be6b --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/adders/ITileAdder.java @@ -0,0 +1,13 @@ +package com.gtnewhorizon.structurelib.structure.adders; + +import net.minecraft.tileentity.TileEntity; + +public interface ITileAdder { + /** + * Callback to add hatch, needs to check if tile is valid (and add it) + * + * @param tileEntity tile + * @return managed to add hatch (structure still valid) + */ + boolean apply(T t, TileEntity tileEntity); +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java b/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java new file mode 100644 index 00000000..80a99eb1 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java @@ -0,0 +1,133 @@ +package com.gtnewhorizon.structurelib.util; + +import net.minecraft.dispenser.IPosition; +import net.minecraftforge.common.util.ForgeDirection; + +public class Vec3Impl implements Comparable { + public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0); + private final int val0; + private final int val1; + private final int val2; + + public Vec3Impl(int in0, int in1, int in2) { + this.val0 = in0; + this.val1 = in1; + this.val2 = in2; + } + + public int compareTo(Vec3Impl o) { + return val1 == o.val1 ? val2 == o.val2 ? val0 - o.val0 : val2 - o.val2 : val1 - o.val1; + } + + /** + * Gets the coordinate. + */ + public int get(int index) { + switch (index){ + case 0: return val0; + case 1: return val1; + case 2: return val2; + default: return 0; + } + } + + /** + * Gets the X coordinate. + */ + public int get0() { + return this.val0; + } + + /** + * Gets the Y coordinate. + */ + public int get1() { + return this.val1; + } + + /** + * Gets the Z coordinate. + */ + public int get2() { + return this.val2; + } + + public Vec3Impl offset(ForgeDirection facing, int n) { + return n == 0 ? this : new Vec3Impl(val0 + facing.offsetX * n, val1 + facing.offsetY * n, val2 + facing.offsetZ * n); + } + + public Vec3Impl add(Vec3Impl pos) { + return new Vec3Impl(val0 + pos.val0, val1 + pos.val1, val2 + pos.val2); + } + + public Vec3Impl sub(Vec3Impl pos) { + return new Vec3Impl(val0 - pos.val0, val1 - pos.val1, val2 - pos.val2); + } + + public Vec3Impl add(int pos0,int pos1,int pos2) { + return new Vec3Impl(val0 + pos0, val1 + pos1, val2 + pos2); + } + + public Vec3Impl sub(int pos0,int pos1,int pos2) { + return new Vec3Impl(val0 - pos0, val1 - pos1, val2 - pos2); + } + + public Vec3Impl crossProduct(Vec3Impl vec) { + return new Vec3Impl(val1 * vec.val2 - val2 * vec.val1, val2 * vec.val0 - val0 * vec.val2, + val0 * vec.val1 - val1 * vec.val0); + } + + public boolean withinDistance(Vec3Impl to, double distance) { + return this.distanceSq(to.val0, to.val1, to.val2, false) < distance * distance; + } + + public boolean withinDistance(IPosition to, double distance) { + return this.distanceSq(to.getX(), to.getY(), to.getZ(), true) < distance * distance; + } + + public double distanceSq(Vec3Impl to) { + return this.distanceSq(to.val0, to.val1, to.val2, true); + } + + public double distanceSq(IPosition to, boolean useCenter) { + return this.distanceSq(to.getX(), to.getY(), to.getZ(), useCenter); + } + + public double distanceSq(double x, double y, double z, boolean useCenter) { + double d0 = useCenter ? 0.5D : 0.0D; + double d1 = (double)val0 + d0 - x; + double d2 = (double)val1 + d0 - y; + double d3 = (double)val2 + d0 - z; + return d1 * d1 + d2 * d2 + d3 * d3; + } + + public int manhattanDistance(Vec3Impl to) { + float f = (float)Math.abs(to.val0 - val0); + float f1 = (float)Math.abs(to.val1 - val1); + float f2 = (float)Math.abs(to.val2 - val2); + return (int)(f + f1 + f2); + } + + @Override + public String toString() { + return "Vec3[" + val0 + ", " + val1 + ", " + val2 + "]"; + } + + public Vec3Impl abs() { + return new Vec3Impl(Math.abs(val0),Math.abs(val1),Math.abs(val2)); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o instanceof Vec3Impl) { + Vec3Impl vec3i = (Vec3Impl)o; + return val0 == vec3i.val0 && val1 == vec3i.val1 && val2 == vec3i.val2; + } + return false; + } + + public int hashCode() { + return (val1 + val2 * 31) * 31 + val0; + } +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizon/structurelib/util/XSTR.java b/src/main/java/com/gtnewhorizon/structurelib/util/XSTR.java new file mode 100644 index 00000000..099c87d9 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/util/XSTR.java @@ -0,0 +1,278 @@ +package com.gtnewhorizon.structurelib.util; +/* + A subclass of java.util.random that implements the Xorshift random number + generator +

+ - it is 30% faster than the generator from Java's library - it produces + random sequences of higher quality than java.util.Random - this class also + provides a clone() function +

+ Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt(); + //pull a random number +

+ To use the class in legacy code, you may also instantiate an XSRandom object + and assign it to a java.util.Random object: java.util.Random rand = new + XSRandom(); +

+ for an explanation of the algorithm, see + http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html + + @author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs + * http://www.elmenreich.tk + *

+ * This code is released under the GNU Lesser General Public License Version 3 + * http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; + +/** + * XSTR - Xorshift ThermiteRandom + * Modified by Bogdan-G + * 03.06.2016 + * version 0.0.4 + */ +public class XSTR extends Random { + + private static final long serialVersionUID = 6208727693524452904L; + private long seed; + private static final long GAMMA = 0x9e3779b97f4a7c15L; + private static final int PROBE_INCREMENT = 0x9e3779b9; + private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; + private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53) + private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24) + private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L); + public static final XSTR XSTR_INSTANCE=new XSTR(){ + @Override + public synchronized void setSeed(long seed) { + if(!Thread.currentThread().getStackTrace()[2].getClassName().equals(Random.class.getName())) { + throw new NoSuchMethodError("This is meant to be shared!, leave seed state alone!"); + } + } + }; + + /* + MODIFIED BY: Robotia + Modification: Implemented Random class seed generator + */ + + /** + * Creates a new pseudo random number generator. The seed is initialized to + * the current time, as if by + * setSeed(System.currentTimeMillis());. + */ + public XSTR() { + this(seedUniquifier() ^ System.nanoTime()); + } + + private static long seedUniquifier() { + // L'Ecuyer, "Tables of Linear Congruential Generators of + // Different Sizes and Good Lattice Structure", 1999 + while (true) { + long current = seedUniquifier.get(); + long next = current * 181783497276652981L; + if (seedUniquifier.compareAndSet(current, next)) { + return next; + } + } + } + + /** + * Creates a new pseudo random number generator, starting with the specified + * seed, using setSeed(seed);. + * + * @param seed the initial seed + */ + public XSTR(long seed) { + this.seed = seed; + } + + @Override + public boolean nextBoolean() { + return next(1) != 0; + } + + @Override + public double nextDouble() { + return (((long) next(26) << 27) + next(27)) * DOUBLE_UNIT; + } + + /** + * Returns the current state of the seed, can be used to clone the object + * + * @return the current seed + */ + public synchronized long getSeed() { + return seed; + } + + /** + * Sets the seed for this pseudo random number generator. As described + * above, two instances of the same random class, starting with the same + * seed, produce the same results, if the same methods are called. + * + * @param seed the new seed + */ + @Override + public synchronized void setSeed(long seed) { + this.seed = seed; + } + + /** + * @return Returns an XSRandom object with the same state as the original + */ + @Override + public XSTR clone() { + return new XSTR(getSeed()); + } + + /** + * Implementation of George Marsaglia's elegant Xorshift random generator + * 30% faster and better quality than the built-in java.util.random see also + * see http://www.javamex.com/tutorials/random_numbers/xorshift.shtml + * + * @param nbits will shift nbits bits + * @return next seed + */ + @Override + public int next(int nbits) { + long x = seed; + x ^= x << 21; + x ^= x >>> 35; + x ^= x << 4; + seed = x; + x &= (1L << nbits) - 1; + return (int) x; + } + + private boolean haveNextNextGaussian = false; + private double nextNextGaussian = 0; + + @Override + public synchronized double nextGaussian() { + // See Knuth, ACP, Section 3.4.1 Algorithm C. + if (haveNextNextGaussian) { + haveNextNextGaussian = false; + return nextNextGaussian; + } else { + double v1, v2, vs; + do { + v1 = 2 * nextDouble() - 1; // between -1 and 1 + v2 = 2 * nextDouble() - 1; // between -1 and 1 + vs = v1 * v1 + v2 * v2; + } while (vs >= 1 || vs == 0); + double multiplier = StrictMath.sqrt(-2 * StrictMath.log(vs) / vs); + nextNextGaussian = v2 * multiplier; + haveNextNextGaussian = true; + return v1 * multiplier; + } + } + + /** + * Returns a pseudorandom, uniformly distributed {@code int} value between 0 + * (inclusive) and the specified value (exclusive), drawn from this random + * number generator's sequence. The general contract of {@code nextInt} is + * that one {@code int} value in the specified range is pseudorandomly + * generated and returned. All {@code bound} possible {@code int} values are + * produced with (approximately) equal probability. The method + * {@code nextInt(int bound)} is implemented by class {@code Random} as if + * by: + *

 {@code
+     * public int nextInt(int bound) {
+     *   if (bound <= 0)
+     *     throw new IllegalArgumentException("bound must be positive");
+     *
+     *   if ((bound & -bound) == bound)  // i.e., bound is a power of 2
+     *     return (int)((bound * (long)next(31)) >> 31);
+     *
+     *   int bits, val;
+     *   do {
+     *       bits = next(31);
+     *       val = bits % bound;
+     *   } while (bits - val + (bound-1) < 0);
+     *   return val;
+     * }}
+ * + *

The hedge "approx + * imately" is used in the foregoing description only because the next + * method is only approximately an unbiased source of independently chosen + * bits. If it were a perfect source of randomly chosen bits, then the + * algorithm shown would choose {@code int} values from the stated range + * with perfect uniformity. + *

+ * The algorithm is slightly tricky. It rejects values that would result in + * an uneven distribution (due to the fact that 2^31 is not divisible by n). + * The probability of a value being rejected depends on n. The worst case is + * n=2^30+1, for which the probability of a reject is 1/2, and the expected + * number of iterations before the loop terminates is 2. + *

+ * The algorithm treats the case where n is a power of two specially: it + * returns the correct number of high-order bits from the underlying + * pseudo-random number generator. In the absence of special treatment, the + * correct number of low-order bits would be returned. Linear + * congruential pseudo-random number generators such as the one implemented + * by this class are known to have short periods in the sequence of values + * of their low-order bits. Thus, this special case greatly increases the + * length of the sequence of values returned by successive calls to this + * method if n is a small power of two. + * + * @param bound the upper bound (exclusive). Must be positive. + * @return the next pseudorandom, uniformly distributed {@code int} value + * between zero (inclusive) and {@code bound} (exclusive) from this random + * number generator's sequence + * @throws IllegalArgumentException if bound is not positive + * @since 1.2 + */ + @Override + public int nextInt(int bound) { + //if (bound <= 0) { + //throw new RuntimeException("BadBound"); + //} + + /*int r = next(31); + int m = bound - 1; + if ((bound & m) == 0) // i.e., bound is a power of 2 + { + r = (int) ((bound * (long) r) >> 31); + } else { + for (int u = r; + u - (r = u % bound) + m < 0; + u = next(31)) + ; + } + return r;*/ + //speedup, new nextInt ~+40% + long last = seed ^ seed << 21; + last ^= last >>> 35; + last ^= last << 4; + seed = last; + int out = (int) last % bound; + return out < 0 ? -out : out; + } + + @Override + public int nextInt() { + return next(32); + } + + @Override + public float nextFloat() { + return next(24) * FLOAT_UNIT; + } + + @Override + public long nextLong() { + // it's okay that the bottom word remains signed. + return ((long) next(32) << 32) + next(32); + } + + @Override + public void nextBytes(byte[] bytes_arr) { + for (int iba = 0, lenba = bytes_arr.length; iba < lenba; ) { + for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba-- > 0; rndba >>= Byte.SIZE) { + bytes_arr[iba++] = (byte) rndba; + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info new file mode 100644 index 00000000..eb7eba1a --- /dev/null +++ b/src/main/resources/mcmod.info @@ -0,0 +1,16 @@ +[ +{ + "modid": "structurelib", + "name": "StructureLib", + "description": "Multiblocks with ease", + "version": "${version}", + "mcversion": "${mcversion}", + "url": "", + "updateUrl": "", + "authorList": ["glee8e"], + "credits": "", + "logoFile": "", + "screenshots": [], + "dependencies": ["Thaumcraft"] +} +] \ No newline at end of file