Compare commits

..
8 Commits
Author SHA1 Message Date
Martin Robertz ebae6436e6 fix +sa + update 2025-04-09 19:14:03 +02:00
Glease 3805ae664c bruh
Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2025-04-09 19:04:05 +02:00
Glease d019b9558b change NEI to a implementation dependency
Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2025-04-09 19:04:05 +02:00
Glease 22cef2e28f spotless
Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2025-04-09 19:04:05 +02:00
Martin Robertz d05ac45924 Merge remote-tracking branch 'origin/feature/channel-desc' into dev
# Conflicts:
#	src/main/java/com/gtnewhorizon/structurelib/StructureLib.java
#	src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
#	src/main/resources/assets/structurelib/lang/en_US.lang
2025-04-09 19:03:59 +02:00
reign bd76be4f6f Comment out GT5U Dependency 2025-04-08 10:58:39 -04:00
reign d3e258dd30 Remove logs and found workaround for updating mode bug 2025-04-08 10:56:50 -04:00
reign 0999964f1a Add removing and updating modes to ItemConstructableTrigger 2025-04-07 21:03:28 -04:00
55 changed files with 810 additions and 595 deletions

No files matched your search

View File
File mode changed.
-17
View File
@@ -1,17 +0,0 @@
name: Optimize images
on:
pull_request:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
push:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
jobs:
optimize-images:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
secrets: inherit
+1 -2
View File
@@ -1,7 +1,7 @@
javadoc {
javadocTool = javaToolchains.javadocToolFor {
// Last version with frames, but it also has search
languageVersion = JavaLanguageVersion.of(11)
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.ADOPTIUM
}
@@ -20,7 +20,6 @@ javadoc {
overview "src/main/javadoc/overview.html"
links 'https://docs.oracle.com/javase/8/docs/api/'
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
addBooleanOption("-frames", true)
}
}
+6 -3
View File
@@ -1,10 +1,13 @@
// Add your dependencies here
dependencies {
api("com.github.GTNewHorizons:GTNHLib:0.9.47:dev")
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev")
api("com.github.GTNewHorizons:GTNHLib:0.6.19:dev")
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.38-GTNH:dev")
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.227:dev") {
// exclude group: "com.github.GTNewHorizons", module: "StructureLib"
// }
}
+6 -41
View File
@@ -37,35 +37,11 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# Select a default username for testing your mod. You can always override this per-run by running
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
developmentEnvironmentUserName = "Developer"
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -120,9 +96,7 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
mixinsPackage = mixins
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -177,7 +151,7 @@ modrinthProjectId =
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations =
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
@@ -198,12 +172,6 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -233,6 +201,3 @@ curseForgeRelations =
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
-12
View File
@@ -1,12 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVersion=25
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored Executable → Regular
+6 -3
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,6 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -171,6 +172,7 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -210,7 +212,8 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -1
View File
@@ -70,10 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.38'
}
@@ -20,6 +20,7 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.culling.Frustrum;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -42,17 +43,23 @@ import org.lwjgl.opengl.GL11;
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class ClientProxy extends CommonProxy {
@@ -259,6 +266,13 @@ public class ClientProxy extends CommonProxy {
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
}
@Override
public void init(FMLInitializationEvent e) {
super.init(e);
MinecraftForge.EVENT_BUS.register(new KeybindHandler());
}
static void markTextureUsed(IIcon icon) {
if (StructureLib.COMPAT instanceof IStructureCompat)
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
@@ -654,4 +668,30 @@ public class ClientProxy extends CommonProxy {
p.endSection();
}
}
public static class KeybindHandler {
private static final KeyBinding CYCLE_PROJECTOR_MODE = new KeyBinding(
"item.structurelib.constructableTrigger.keys.cycleMode",
0,
"item.structurelib.constructableTrigger.keyCategory");
public KeybindHandler() {
FMLCommonHandler.instance().bus().register(this);
// If somebody wants to make a UI element for this, be my guest
ClientRegistry.registerKeyBinding(CYCLE_PROJECTOR_MODE);
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
public void key(InputEvent.KeyInputEvent event) {
if (CYCLE_PROJECTOR_MODE.isPressed()) {
cycleProjectorMode();
}
}
private static void cycleProjectorMode() {
StructureLib.net.sendToServer(new StructureProjectorCycleModeMessage());
}
}
}
@@ -17,6 +17,7 @@ import net.minecraftforge.common.MinecraftForge;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@@ -64,6 +65,8 @@ public class CommonProxy {
public void preInit(FMLPreInitializationEvent e) {}
public void init(FMLInitializationEvent e) {}
public long getOverworldTime() {
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
}
@@ -27,12 +27,14 @@ import com.gtnewhorizon.structurelib.net.AlignmentMessage;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import com.gtnewhorizon.structurelib.util.InventoryUtility;
import com.gtnewhorizon.structurelib.util.XSTR;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
@@ -77,6 +79,11 @@ public class StructureLib {
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
net.registerMessage(
StructureProjectorCycleModeMessage.Handler.class,
StructureProjectorCycleModeMessage.class,
6,
Side.SERVER);
try {
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
@@ -128,6 +135,11 @@ public class StructureLib {
DevelopHelper.onPreInit();
}
@Mod.EventHandler
public void init(FMLInitializationEvent e) {
proxy.init(e);
}
@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent e) {
proxy.loadComplete(e);
@@ -9,6 +9,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.IIcon;
import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler;
@@ -380,9 +381,7 @@ public class StructureLibAPI {
ChannelDescription.item(channel, channelValue, stack);
AnimatedTooltipHandler.addItemTooltip(
stack,
AnimatedTooltipHandler.translatedText("structurelib.tooltip.channelvalue", channelValue, channel));
AnimatedTooltipHandler.addItemTooltip(
stack,
AnimatedTooltipHandler.translatedText("structurelib.tooltip.indicator_dnd", channelValue, channel));
() -> StatCollector
.translateToLocalFormatted("structurelib.tooltip.channelvalue", channelValue, channel));
}
}
@@ -14,28 +14,8 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
public interface IAlignmentLimits {
/**
* Can be placed in any orientation.
*/
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
/**
* Must be standing upright; i.e., top side of the block facing up.
*/
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
/**
* Can be in any orientation except upside down; i.e., top side of the block facing down.
*/
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
/**
* Front side of the block must be facing up.
*/
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
@@ -57,12 +57,12 @@ public class ConstructableUtility {
}
IConstructable constructable = null;
if (tTileEntity instanceof IConstructableProvider)
constructable = ((IConstructableProvider) tTileEntity).getConstructable();
else if (tTileEntity instanceof IConstructable) {
constructable = (IConstructable) tTileEntity;
if (tTileEntity instanceof IConstructableProvider aConstructableProvider)
constructable = aConstructableProvider.getConstructable();
else if (tTileEntity instanceof IConstructable aConstructable) {
constructable = aConstructable;
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
ExtendedFacing facing = tTileEntity instanceof IAlignment alignment ? alignment.getExtendedFacing()
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
.toConstructable(tTileEntity, facing);
@@ -70,14 +70,13 @@ public class ConstructableUtility {
if (constructable == null) return false;
if (aPlayer instanceof EntityPlayerMP) {
EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer;
if (aPlayer instanceof EntityPlayerMP playerMP) {
// server side and sneaking (already checked above)
// do construct
if (aPlayer.capabilities.isCreativeMode) {
constructable.construct(aStack, false);
} else if (constructable instanceof ISurvivalConstructable) {
int built = ((ISurvivalConstructable) constructable).survivalConstruct(
} else if (constructable instanceof ISurvivalConstructable aConstructable) {
int built = aConstructable.survivalConstruct(
aStack,
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
@@ -50,10 +50,6 @@ public class BlockHint extends Block {
@Override
public IIcon getIcon(int aSide, int aMeta) {
// For numbered hints, top and bottom are blank
if (aSide <= 1 && aMeta <= 11) {
return hint[13];
}
return hint[aMeta];
}
@@ -1,20 +1,34 @@
package com.gtnewhorizon.structurelib.core;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.structurelib.mixins.Mixins;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.MCVersion("1.7.10")
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public static final Logger log = LogManager.getLogger("StructureLibCore");
@Override
public String getMixinConfig() {
return "mixins.structurelib.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Mixins.getEarlyMixins(loadedCoreMods);
}
@Override
public String[] getASMTransformerClass() {
return null;
return new String[0];
}
@Override
@@ -36,14 +50,4 @@ public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
return "mixins.structurelib.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
}
}
@@ -1,5 +1,17 @@
package com.gtnewhorizon.structurelib.gui;
import static org.lwjgl.opengl.ARBImaging.*;
import static org.lwjgl.opengl.ARBImaging.GL_SEPARABLE_2D;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_ALPHA_TEST_FUNC;
import static org.lwjgl.opengl.GL12.GL_RESCALE_NORMAL;
import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL14.*;
import static org.lwjgl.opengl.GL14.GL_BLEND_SRC_RGB;
import static org.lwjgl.opengl.GL15.GL_FOG_COORD_ARRAY;
import static org.lwjgl.opengl.GL20.*;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
@@ -13,7 +25,6 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiTextField;
@@ -31,7 +42,7 @@ import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizon.structurelib.ChannelDescription;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
@@ -55,7 +66,6 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
private static final int WIPE_BTN = 2;
private static final int KEY_MAX_WIDTH = 50;
private static final int INFO_BOX_SIZE = 11;
private final ItemStack trigger;
private final GuiChannelsList list;
private final GuiAutoCompleteList autoCompleteList;
@@ -63,13 +73,12 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
private final TIntObjectMap<Map<String, String>> trimCache = new TIntObjectHashMap<>();
private GuiTextField key, value;
protected int guiTop, guiLeft;
private List<String> info;
public GuiScreenConfigureChannels(Container container, ItemStack trigger) {
super(container);
this.trigger = trigger;
list = new GuiChannelsList(152, 91, 12, 21, 14);
list = new GuiChannelsList(152, 100, 12, 12, 14);
list.addSelectionListener((list, selectedIndex) -> {
Entry<String, Integer> e = list.getElementAt(selectedIndex);
key.setText(e.getKey());
@@ -164,18 +173,33 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
list.onGuiInit(this);
autoCompleteList.onGuiInit(this);
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
addButton(
new GuiButton(
ADD_BTN,
guiLeft + 12,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.add")));
addButton(
new GuiButton(
UNSET_BTN,
guiLeft + 65,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
addButton(
new GuiButton(
WIPE_BTN,
guiLeft + 118,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
updateButtons();
tooltipSplitCache.clear();
info = StreamSupport
.stream(
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
false)
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
.collect(Collectors.toList());
}
@Override
@@ -218,7 +242,8 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
actionPerformed(but);
}
public List<GuiButton> getButtonList() {
@SuppressWarnings("unchecked")
private List<GuiButton> getButtonList() {
return buttonList;
}
@@ -261,11 +286,6 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
switch (aKey) {
case Keyboard.KEY_TAB:
if (key.isFocused()) {
if (autoCompleteList.getSelectedElement() != null) {
key.setText(autoCompleteList.getSelectedElement());
return;
}
key.setFocused(false);
value.setFocused(true);
} else {
@@ -279,8 +299,6 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
key.setText(autoCompleteList.getSelectedElement());
key.setFocused(false);
value.setFocused(true);
value.setText("1");
value.setSelectionPos(0);
return;
}
GuiButton add = getButtonList().get(ADD_BTN);
@@ -303,10 +321,11 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
}
break;
}
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
if (key.textboxKeyTyped(aChar, aKey)) {
updateButtons();
return;
}
if (value.textboxKeyTyped(aChar, aKey)) return;
super.keyTyped(aChar, aKey);
}
@@ -317,8 +336,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
: I18n.format(I18N_PREFIX + "add");
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
&& !StringUtils.isBlank(value.getText())
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText())
&& Integer.parseInt(value.getText()) > 0;
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
@@ -378,10 +396,17 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
int topLeftY = (this.height - this.getYSize()) / 2;
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
list.drawScreen(mX, mY, partialTick);
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.key"),
guiLeft + 12,
guiTop + 122,
0);
key.drawTextBox();
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.value"),
guiLeft + 12,
guiTop + 142,
0);
value.drawTextBox();
}
@@ -405,11 +430,6 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
if (highlight != null) {
drawChannelDescriptionTooltip(highlight);
}
if (mouseX > xSize + guiLeft - INFO_BOX_SIZE - 12 && mouseX < xSize + guiLeft - 12
&& mouseY > guiTop + 7
&& mouseY < guiTop + INFO_BOX_SIZE + 7) {
drawHoveringText(info, mouseX, mouseY, fontRendererObj);
}
}
private void drawChannelDescriptionTooltip(String candidate) {
@@ -417,6 +437,105 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
drawHoveringText(tooltip, guiLeft + 151 + 12, guiTop + 122, fontRendererObj);
}
public static void debugPrintGLEnable() {
Map<Integer, String> functions = ImmutableMap.<Integer, String>builder().put(GL11.GL_NEVER, "GL_NEVER")
.put(GL11.GL_LESS, "GL_LESS").put(GL11.GL_EQUAL, "GL_EQUAL").put(GL11.GL_LEQUAL, "GL_LEQUAL")
.put(GL11.GL_GREATER, "GL_GREATER").put(GL11.GL_NOTEQUAL, "GL_NOTEQUAL")
.put(GL11.GL_GEQUAL, "GL_GEQUAL").put(GL11.GL_ALWAYS, "GL_ALWAYS").put(GL11.GL_ZERO, "GL_ZERO")
.put(GL11.GL_ONE, "GL_ONE").put(GL11.GL_SRC_COLOR, "GL_SRC_COLOR")
.put(GL11.GL_ONE_MINUS_SRC_COLOR, "GL_ONE_MINUS_SRC_COLOR").put(GL11.GL_DST_COLOR, "GL_DST_COLOR")
.put(GL11.GL_ONE_MINUS_DST_COLOR, "GL_ONE_MINUS_DST_COLOR").put(GL11.GL_SRC_ALPHA, "GL_SRC_ALPHA")
.put(GL11.GL_ONE_MINUS_SRC_ALPHA, "GL_ONE_MINUS_SRC_ALPHA").put(GL11.GL_DST_ALPHA, "GL_DST_ALPHA")
.put(GL11.GL_ONE_MINUS_DST_ALPHA, "GL_ONE_MINUS_DST_ALPHA")
.put(GL11.GL_CONSTANT_COLOR, "GL_CONSTANT_COLOR")
.put(GL11.GL_ONE_MINUS_CONSTANT_COLOR, "GL_ONE_MINUS_CONSTANT_COLOR")
.put(GL11.GL_CONSTANT_ALPHA, "GL_CONSTANT_ALPHA")
.put(GL11.GL_ONE_MINUS_CONSTANT_ALPHA, "GL_ONE_MINUS_CONSTANT_ALPHA")
.put(GL11.GL_SRC_ALPHA_SATURATE, "GL_SRC_ALPHA_SATURATE").build();
System.out.println("GL_ALPHA_TEST: " + GL11.glIsEnabled(GL_ALPHA_TEST));
System.out.println("GL_AUTO_NORMAL: " + GL11.glIsEnabled(GL_AUTO_NORMAL));
System.out.println("GL_BLEND: " + GL11.glIsEnabled(GL_BLEND));
System.out.println("GL_COLOR_ARRAY: " + GL11.glIsEnabled(GL_COLOR_ARRAY));
System.out.println("GL_COLOR_LOGIC_OP: " + GL11.glIsEnabled(GL_COLOR_LOGIC_OP));
System.out.println("GL_COLOR_MATERIAL: " + GL11.glIsEnabled(GL_COLOR_MATERIAL));
System.out.println("GL_COLOR_SUM: " + GL11.glIsEnabled(GL_COLOR_SUM));
System.out.println("GL_COLOR_TABLE: " + GL11.glIsEnabled(GL_COLOR_TABLE));
System.out.println("GL_CONVOLUTION_1D: " + GL11.glIsEnabled(GL_CONVOLUTION_1D));
System.out.println("GL_CONVOLUTION_2D: " + GL11.glIsEnabled(GL_CONVOLUTION_2D));
System.out.println("GL_CULL_FACE: " + GL11.glIsEnabled(GL_CULL_FACE));
System.out.println("GL_DEPTH_TEST: " + GL11.glIsEnabled(GL_DEPTH_TEST));
System.out.println("GL_DITHER: " + GL11.glIsEnabled(GL_DITHER));
System.out.println("GL_EDGE_FLAG_ARRAY: " + GL11.glIsEnabled(GL_EDGE_FLAG_ARRAY));
System.out.println("GL_FOG: " + GL11.glIsEnabled(GL_FOG));
System.out.println("GL_FOG_COORD_ARRAY: " + GL11.glIsEnabled(GL_FOG_COORD_ARRAY));
System.out.println("GL_HISTOGRAM: " + GL11.glIsEnabled(GL_HISTOGRAM));
System.out.println("GL_INDEX_ARRAY: " + GL11.glIsEnabled(GL_INDEX_ARRAY));
System.out.println("GL_INDEX_LOGIC_OP: " + GL11.glIsEnabled(GL_INDEX_LOGIC_OP));
System.out.println("GL_LIGHTING: " + GL11.glIsEnabled(GL_LIGHTING));
System.out.println("GL_LINE_SMOOTH: " + GL11.glIsEnabled(GL_LINE_SMOOTH));
System.out.println("GL_LINE_STIPPLE: " + GL11.glIsEnabled(GL_LINE_STIPPLE));
System.out.println("GL_MAP1_COLOR_4: " + GL11.glIsEnabled(GL_MAP1_COLOR_4));
System.out.println("GL_MAP1_INDEX: " + GL11.glIsEnabled(GL_MAP1_INDEX));
System.out.println("GL_MAP1_NORMAL: " + GL11.glIsEnabled(GL_MAP1_NORMAL));
System.out.println("GL_MAP1_TEXTURE_COORD_1: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_1));
System.out.println("GL_MAP1_TEXTURE_COORD_2: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_2));
System.out.println("GL_MAP1_TEXTURE_COORD_3: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_3));
System.out.println("GL_MAP1_TEXTURE_COORD_4: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_4));
System.out.println("GL_MAP2_COLOR_4: " + GL11.glIsEnabled(GL_MAP2_COLOR_4));
System.out.println("GL_MAP2_INDEX: " + GL11.glIsEnabled(GL_MAP2_INDEX));
System.out.println("GL_MAP2_NORMAL: " + GL11.glIsEnabled(GL_MAP2_NORMAL));
System.out.println("GL_MAP2_TEXTURE_COORD_1: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_1));
System.out.println("GL_MAP2_TEXTURE_COORD_2: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_2));
System.out.println("GL_MAP2_TEXTURE_COORD_3: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_3));
System.out.println("GL_MAP2_TEXTURE_COORD_4: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_4));
System.out.println("GL_MAP2_VERTEX_3: " + GL11.glIsEnabled(GL_MAP2_VERTEX_3));
System.out.println("GL_MAP2_VERTEX_4: " + GL11.glIsEnabled(GL_MAP2_VERTEX_4));
System.out.println("GL_MINMAX: " + GL11.glIsEnabled(GL_MINMAX));
System.out.println("GL_MULTISAMPLE: " + GL11.glIsEnabled(GL_MULTISAMPLE));
System.out.println("GL_NORMAL_ARRAY: " + GL11.glIsEnabled(GL_NORMAL_ARRAY));
System.out.println("GL_NORMALIZE: " + GL11.glIsEnabled(GL_NORMALIZE));
System.out.println("GL_POINT_SMOOTH: " + GL11.glIsEnabled(GL_POINT_SMOOTH));
System.out.println("GL_POINT_SPRITE: " + GL11.glIsEnabled(GL_POINT_SPRITE));
System.out.println("GL_POLYGON_SMOOTH: " + GL11.glIsEnabled(GL_POLYGON_SMOOTH));
System.out.println("GL_POLYGON_OFFSET_FILL: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_FILL));
System.out.println("GL_POLYGON_OFFSET_LINE: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_LINE));
System.out.println("GL_POLYGON_OFFSET_POINT: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_POINT));
System.out.println("GL_POLYGON_STIPPLE: " + GL11.glIsEnabled(GL_POLYGON_STIPPLE));
System.out.println("GL_POST_COLOR_MATRIX_COLOR_TABLE: " + GL11.glIsEnabled(GL_POST_COLOR_MATRIX_COLOR_TABLE));
System.out.println("GL_POST_CONVOLUTION_COLOR_TABLE: " + GL11.glIsEnabled(GL_POST_CONVOLUTION_COLOR_TABLE));
System.out.println("GL_RESCALE_NORMAL: " + GL11.glIsEnabled(GL_RESCALE_NORMAL));
System.out.println("GL_SAMPLE_ALPHA_TO_COVERAGE: " + GL11.glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
System.out.println("GL_SAMPLE_ALPHA_TO_ONE: " + GL11.glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE));
System.out.println("GL_SAMPLE_COVERAGE: " + GL11.glIsEnabled(GL_SAMPLE_COVERAGE));
System.out.println("GL_SCISSOR_TEST: " + GL11.glIsEnabled(GL_SCISSOR_TEST));
System.out.println("GL_SECONDARY_COLOR_ARRAY: " + GL11.glIsEnabled(GL_SECONDARY_COLOR_ARRAY));
System.out.println("GL_SEPARABLE_2D: " + GL11.glIsEnabled(GL_SEPARABLE_2D));
System.out.println("GL_STENCIL_TEST: " + GL11.glIsEnabled(GL_STENCIL_TEST));
System.out.println("GL_TEXTURE_1D: " + GL11.glIsEnabled(GL_TEXTURE_1D));
System.out.println("GL_TEXTURE_2D: " + GL11.glIsEnabled(GL_TEXTURE_2D));
System.out.println("GL_TEXTURE_3D: " + GL11.glIsEnabled(GL_TEXTURE_3D));
System.out.println("GL_TEXTURE_COORD_ARRAY: " + GL11.glIsEnabled(GL_TEXTURE_COORD_ARRAY));
System.out.println("GL_TEXTURE_CUBE_MAP: " + GL11.glIsEnabled(GL_TEXTURE_CUBE_MAP));
System.out.println("GL_TEXTURE_GEN_Q: " + GL11.glIsEnabled(GL_TEXTURE_GEN_Q));
System.out.println("GL_TEXTURE_GEN_R: " + GL11.glIsEnabled(GL_TEXTURE_GEN_R));
System.out.println("GL_TEXTURE_GEN_S: " + GL11.glIsEnabled(GL_TEXTURE_GEN_S));
System.out.println("GL_TEXTURE_GEN_T: " + GL11.glIsEnabled(GL_TEXTURE_GEN_T));
System.out.println("GL_VERTEX_ARRAY: " + GL11.glIsEnabled(GL_VERTEX_ARRAY));
System.out.println("GL_VERTEX_PROGRAM_POINT_SIZE: " + GL11.glIsEnabled(GL_VERTEX_PROGRAM_POINT_SIZE));
System.out.println("GL_VERTEX_PROGRAM_TWO_SIDE: " + GL11.glIsEnabled(GL_VERTEX_PROGRAM_TWO_SIDE));
System.out.println("Depth Function: " + functions.get(GL11.glGetInteger(GL_DEPTH_FUNC)));
System.out.printf(
"Blend Function: DST: Alpha %s RGB %s, SRC: Alpha %s RGB %s\n",
functions.get(GL11.glGetInteger(GL_BLEND_DST_ALPHA)),
functions.get(GL11.glGetInteger(GL_BLEND_DST_RGB)),
functions.get(GL11.glGetInteger(GL_BLEND_SRC_ALPHA)),
functions.get(GL11.glGetInteger(GL_BLEND_SRC_RGB)));
System.out.println(
"Alpha Test Function: " + functions.get(GL11.glGetInteger(GL_ALPHA_TEST_FUNC))
+ ", Reference: "
+ GL11.glGetDouble(GL11.GL_ALPHA_TEST_REF));
}
private List<String> getChannelDescriptionTooltip(String candidate) {
List<String> tooltip = new ArrayList<>();
int maxLine = width - (guiLeft + 151 + 12) - 12;
@@ -424,7 +543,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
tooltip.addAll(
fontRendererObj.listFormattedStringToWidth(
StatCollector.translateToLocalFormatted(
I18N_PREFIX + "channels.from",
"item.structurelib.constructableTrigger.gui.channels.from",
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
maxLine));
tooltip.addAll(
@@ -7,9 +7,14 @@ import static net.minecraft.util.StatCollector.translateToLocalFormatted;
import java.util.List;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.util.IIcon;
import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
import org.lwjgl.input.Keyboard;
@@ -20,20 +25,80 @@ import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtilit
public class ItemConstructableTrigger extends Item {
private static final String TAG_MODE = "mode";
private static final IIcon[] textures = new IIcon[3];
public ItemConstructableTrigger() {
setUnlocalizedName("structurelib.constructableTrigger");
setTextureName(MOD_ID + ":itemConstructableTrigger");
setCreativeTab(StructureLib.creativeTab);
}
public static void cycleMode(ItemStack held) {
if (held.getTagCompound() == null) held.setTagCompound(new NBTTagCompound());
if (!held.getTagCompound().hasKey(TAG_MODE)) held.getTagCompound().setTag(TAG_MODE, new NBTTagInt(0));
held.getTagCompound().setInteger(TAG_MODE, (held.getTagCompound().getInteger(TAG_MODE) + 1) % 3);
}
@Override
public void registerIcons(IIconRegister register) {
textures[0] = register.registerIcon(MOD_ID + ":constructableTrigger/BUILDING");
textures[1] = register.registerIcon(MOD_ID + ":constructableTrigger/UPDATING");
textures[2] = register.registerIcon(MOD_ID + ":constructableTrigger/REMOVING");
}
@Override
public IIcon getIcon(ItemStack stack, int renderPass, EntityPlayer player, ItemStack usingItem, int useRemaining) {
return getIcon(stack, renderPass);
}
@Override
public IIcon getIcon(ItemStack stack, int pass) {
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
return textures[0];
}
return textures[stack.getTagCompound().getInteger(TAG_MODE)];
}
@Override
public String getItemStackDisplayName(ItemStack stack) {
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
return getDisplayName(0);
}
return getDisplayName(stack.getTagCompound().getInteger(TAG_MODE));
}
@Override
public IIcon getIconIndex(ItemStack stack) {
return getIcon(stack, 0);
}
@Override
public IIcon getIconFromDamage(int damage) {
return textures[damage];
}
private String getDisplayName(int mode) {
return StatCollector.translateToLocal("item.structurelib.constructableTrigger.name") + " - "
+ StatCollector.translateToLocal("item.structurelib.constructableTrigger.modes." + mode);
}
public static int getMode(ItemStack held) {
if (held == null || !held.hasTagCompound() || !held.getTagCompound().hasKey(TAG_MODE)) {
// default behavior
return 0;
}
// get user-defined mode
return held.getTagCompound().getInteger(TAG_MODE);
}
@Override
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
if (getMovingObjectPositionFromPlayer(world, player, true) == null) {
// channel gui is opened from server and config gui from client
if (!world.isRemote && !player.isSneaking()) {
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
} else if (world.isRemote && player.isSneaking()) {
if (!world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
if (player.isSneaking()) {
StructureLib.instance().proxy().displayConfigGUI("registries");
} else {
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
}
}
return super.onItemRightClick(stack, world, player);
@@ -0,0 +1,188 @@
package com.gtnewhorizon.structurelib.mixins;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import com.gtnewhorizon.structurelib.core.StructureLibCore;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
public enum Mixins {
BLOCK_CHANGE_NOTIFIER(new Builder("Block Change Notifier").setPhase(Phase.EARLY)
.addMixinClasses("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld")
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH).setApplyIf(() -> true));
private final List<String> mixinClasses;
private final List<TargetedMod> targetedMods;
private final List<TargetedMod> excludedMods;
private final Supplier<Boolean> applyIf;
private final Phase phase;
private final Side side;
Mixins(Builder builder) {
this.mixinClasses = builder.mixinClasses;
this.targetedMods = builder.targetedMods;
this.excludedMods = builder.excludedMods;
this.applyIf = builder.applyIf;
this.phase = builder.phase;
this.side = builder.side;
if (this.mixinClasses.isEmpty()) {
throw new RuntimeException("No mixin class specified for Mixin : " + this.name());
}
if (this.targetedMods.isEmpty()) {
throw new RuntimeException("No targeted mods specified for Mixin : " + this.name());
}
if (this.applyIf == null) {
throw new RuntimeException("No ApplyIf function specified for Mixin : " + this.name());
}
if (this.phase == null) {
throw new RuntimeException("No Phase specified for Mixin : " + this.name());
}
if (this.side == null) {
throw new RuntimeException("No Side function specified for Mixin : " + this.name());
}
}
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.EARLY) {
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following EARLY mixins: {}", notLoading.toString());
return mixins;
}
public static List<String> getLateMixins(Set<String> loadedMods) {
// NOTE: Any targetmod here needs a modid, not a coremod id
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.LATE) {
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following LATE mixins: {}", notLoading.toString());
return mixins;
}
private boolean shouldLoadSide() {
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
}
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return false;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& !loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return true;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
return (shouldLoadSide() && applyIf.get()
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
}
private static class Builder {
private final String name;
private final List<String> mixinClasses = new ArrayList<>();
private final List<TargetedMod> targetedMods = new ArrayList<>();
private final List<TargetedMod> excludedMods = new ArrayList<>();
private Supplier<Boolean> applyIf = null;
private Phase phase = null;
private Side side = null;
public Builder(String name) {
this.name = name;
}
public Builder addMixinClasses(String... mixinClasses) {
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
return this;
}
public Builder setPhase(Phase phase) {
if (this.phase != null) {
throw new RuntimeException("Trying to define Phase twice for " + this.name);
}
this.phase = phase;
return this;
}
public Builder setSide(Side side) {
if (this.side != null) {
throw new RuntimeException("Trying to define Side twice for " + this.name);
}
this.side = side;
return this;
}
public Builder setApplyIf(Supplier<Boolean> applyIf) {
this.applyIf = applyIf;
return this;
}
public Builder addTargetedMod(TargetedMod mod) {
this.targetedMods.add(mod);
return this;
}
public Builder addExcludedMod(TargetedMod mod) {
this.excludedMods.add(mod);
return this;
}
}
private enum Side {
BOTH,
CLIENT,
SERVER
}
private enum Phase {
EARLY,
LATE,
}
}
@@ -0,0 +1,37 @@
package com.gtnewhorizon.structurelib.net;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf;
public class StructureProjectorCycleModeMessage implements IMessage {
@Override
public void fromBytes(ByteBuf buf) {
// no op
}
@Override
public void toBytes(ByteBuf buf) {
// no op
}
public static class Handler implements IMessageHandler<StructureProjectorCycleModeMessage, IMessage> {
@Override
public IMessage onMessage(StructureProjectorCycleModeMessage message, MessageContext ctx) {
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
final ItemStack held = player.getHeldItem();
if (held != null && held.getItem() instanceof ItemConstructableTrigger) {
ItemConstructableTrigger.cycleMode(held);
}
return null;
}
}
}
@@ -319,22 +319,6 @@ public interface IStructureDefinition<T> {
return walker.getBuilt();
}
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
StructureUtility.iterateV2(
getStructureFor(piece),
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
predicate,
"customIterate");
}
/**
* Low level utility.
*
@@ -364,77 +348,47 @@ public interface IStructureDefinition<T> {
}
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
boolean success;
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
"check");
} else {
success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
"check force");
}
boolean success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
checkBlocksIfNotNullForceCheckAllIfTrue ? (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)
: skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
checkBlocksIfNotNullForceCheckAllIfTrue ? "check" : "check force");
if (StructureLibAPI.isDebugEnabled() && success) {
StructureLib.LOGGER
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
}
return success;
} else {
if (hintsOnly) {
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger);
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
}
return true;
}),
"spawnHint");
} else {
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger);
return true;
}),
"placeBlock");
}
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
hintsOnly ? ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger);
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
}
return true;
}) : ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger);
return true;
}),
hintsOnly ? "spawnHint" : "placeBlock");
}
return true;
}
@@ -5,7 +5,6 @@ import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -22,6 +21,7 @@ import net.minecraft.util.IChatComponent;
import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
@@ -43,28 +43,6 @@ public interface IStructureElement<T> {
return true;
}
/**
* Returns a human-readable description of what block(s) this element accepts. Used for diagnostic messages when
* structure checks fail (e.g. "expected Input Bus at (1, 2, 3)").
* <p>
* Unlike {@link #getBlocksToPlace}, which returns the full set of concrete block variants for autoplacing (e.g.
* every tier of Input Bus), this method returns <b>category-level</b> descriptions suitable for display to the
* player (e.g. just "Input Bus"). It also requires no parameters, no world, trigger item, or environment, making it
* usable in contexts where those are unavailable.
* <p>
* Implementations should return <b>lang keys</b> (e.g. {@code "tile.blockGlass.name"}) rather than pre-translated
* display names. The caller is responsible for translating these keys on the client side via
* {@code StatCollector.translateToLocal()}. If a key has no matching translation, {@code translateToLocal} returns
* the input string unchanged, so pre-translated fallback strings also work.
*
* @return a list of accepted block descriptions as lang keys (or display names as fallback), or null if no
* description is available
*/
@Nullable
default List<String> getDescription() {
return null;
}
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);
@@ -160,12 +138,13 @@ public interface IStructureElement<T> {
*/
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
int mode = ItemConstructableTrigger.getMode(trigger);
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
IItemSource source = env.getSource();
EntityPlayer actor = env.getActor();
Consumer<IChatComponent> chatter = env.getChatter();
if (e != null) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
if (e.getStacks() == null) {
ItemStack taken = source.takeOne(e.getPredicate(), true);
return StructureUtility.survivalPlaceBlock(
@@ -182,7 +161,7 @@ public interface IStructureElement<T> {
chatter);
}
for (ItemStack stack : e.getStacks()) {
if (!source.takeOne(stack, true)) continue;
if (mode < 2 && !source.takeOne(stack, true)) continue;
return StructureUtility.survivalPlaceBlock(
stack,
NBTMode.EXACT,
@@ -227,12 +206,6 @@ public interface IStructureElement<T> {
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return IStructureElement.this.spawnHint(t, world, x, y, z, trigger);
}
@Nullable
@Override
public List<String> getDescription() {
return IStructureElement.this.getDescription();
}
};
}
@@ -1,9 +1,7 @@
package com.gtnewhorizon.structurelib.structure;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -63,19 +61,6 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
return false;
}
@Nullable
@Override
default List<String> getDescription() {
Set<String> descriptions = new LinkedHashSet<>();
for (IStructureElement<T> fallback : fallbacks()) {
List<String> desc = fallback.getDescription();
if (desc != null) {
descriptions.addAll(desc);
}
}
return descriptions.isEmpty() ? null : new ArrayList<>(descriptions);
}
@Nullable
@Override
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
@@ -2,25 +2,8 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.world.World;
/**
* A functional interface that is used to iterate over an arbitrary structure.
*/
@FunctionalInterface
public interface IStructureWalker<T> {
interface IStructureWalker<T> {
/**
* Called once per structure element.
*
* @param element The current element
* @param world The current world
* @param x The absolute X position of the element in world coordinates
* @param y The absolute Y position of the element in world coordinates
* @param z The absolute Z position of the element in world coordinates
* @param a The relative A position of the element in structure-local coordinates (pre transform)
* @param b The relative B position of the element in structure-local coordinates (pre transform)
* @param c The relative C position of the element in structure-local coordinates (pre transform)
* @return False to stop iterating, true to continue.
*/
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
@@ -1,6 +1,5 @@
package com.gtnewhorizon.structurelib.structure;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -66,10 +65,4 @@ class LazyStructureElement<T> implements IStructureElementDeferred<T> {
AutoPlaceEnvironment env) {
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return elem != null ? elem.getDescription() : null;
}
}
@@ -148,32 +148,15 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
occupiedSpaces.put(name, occupiedSpace);
boolean hasPlus = false;
boolean hasMinus = false;
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
int write = 0;
for (int read = 0; read < builder.length(); read++) {
char ch = builder.charAt(read);
if (ch == A || ch == B || ch == C) {
continue;
}
if (ch == '+') {
hasPlus = true;
} else if (ch == '-') {
hasMinus = true;
}
if (read != write) builder.setCharAt(write, ch);
write++;
}
builder.setLength(write);
if (hasPlus) {
if (built.contains("+")) {
addElement('+', notAir());
}
if (hasMinus) {
if (built.contains("-")) {
addElement('-', isAir());
}
shapes.put(name, builder.toString());
shapes.put(name, built);
return this;
}
@@ -28,6 +28,7 @@ import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
@@ -36,10 +37,7 @@ import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
@@ -54,6 +52,7 @@ import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
@@ -209,7 +208,11 @@ public class StructureUtility {
@Override
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2);
placeRemoveOrUpdate(
trigger,
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
return true;
}
@@ -222,11 +225,16 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(o, world, x, y, z)) return PlaceResult.SKIP;
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && check(o, world, x, y, z)) return PlaceResult.SKIP;
// user should place anything here.
// maybe make this configurable, but for now we try to take some cobble from user
if (env.getSource().takeOne(new ItemStack(Blocks.cobblestone), false)) {
world.setBlock(x, y, z, Blocks.cobblestone, 0, 2);
placeRemoveOrUpdate(
trigger,
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
}
return PlaceResult.REJECT;
}
@@ -333,27 +341,75 @@ public class StructureUtility {
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
EntityPlayer actor, Consumer<IChatComponent> chatter) {
if (block == null) throw new NullPointerException();
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
int mode = 0;
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
// ...trigger instead of assuming default behavior
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
}
if (mode == 0 && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
if (mode == 1 && world.getTileEntity(x, y, z) instanceof TileEntity) {
// We're in updating mode and the block we're looking at might be important, so let's just leave it alone
// since it's probably not something we want to replace with something else
return PlaceResult.SKIP;
}
Item itemBlock = Item.getItemFromBlock(block);
int itemMeta = itemBlock instanceof ISpecialItemBlock
? ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta)
: meta;
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
if (!s.takeOne(stack, true)) {
if (mode == 1 && !world.isAirBlock(x, y, z)) {
Block blockInWorld = world.getBlock(x, y, z);
if (blockInWorld.isAssociatedBlock(block) && blockInWorld.getDamageValue(world, x, y, z) == meta) {
// This is the block we are looking for, move along
return PlaceResult.SKIP;
}
}
if (mode < 2 && !s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
new ChatComponentTranslation(
"structurelib.autoplace.error.no_simple_block",
stack.func_151000_E()));
return PlaceResult.REJECT;
}
if (block instanceof ICustomBlockSetting) {
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
blockCustom.setBlock(world, x, y, z, meta);
} else if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
if (mode < 2) {
if (block instanceof ICustomBlockSetting) {
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
blockCustom.setBlock(world, x, y, z, meta);
} else {
if (mode == 1 && !world.isAirBlock(x, y, z)) {
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
// ...and drop it to the player
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
// We either weren't able to give the player the block that was there, or we weren't able to
// remove
// the block, either way, we need to reject the update. Also, since we hid the block update from
// the client in an effort to make the exchange as seamless as possible, we need to now notify
// the client if we did successfully remove the block.
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
return PlaceResult.REJECT;
}
}
if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
return PlaceResult.REJECT;
}
if (!s.takeOne(stack, false)) {
// rollback
}
}
if (mode == 2 && world.isAirBlock(x, y, z)) {
return PlaceResult.SKIP;
}
if (mode == 2 || !s.takeOne(stack, false)) {
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
world.setBlockToAir(x, y, z);
if (mode == 2 && !tryGiveOrDropItem(world, actor, toGive)) {
// Something went wrong while trying to give the player the block we removed, let's just fail for now
return PlaceResult.REJECT;
}
}
return PlaceResult.ACCEPT;
}
@@ -445,24 +501,74 @@ public class StructureUtility {
if (stack == null) throw new NullPointerException();
if (stack.stackSize != 1) throw new IllegalArgumentException();
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
if (!assumeStackPresent && !s.takeOne(stack, true)) {
int mode = 0;
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
// ...trigger instead of assuming default behavior
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
}
if (mode == 1 && !world.isAirBlock(x, y, z)) {
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
// We're in updating mode and the block we're looking at might be important, so let's just leave it
// alone
// since it's probably not something we want to replace with something else
return PlaceResult.SKIP;
}
Block blockInWorld = world.getBlock(x, y, z);
ItemStack blockInWorldItem = new ItemStack(blockInWorld);
blockInWorldItem.setItemDamage(blockInWorld.getDamageValue(world, x, y, z));
if (blockInWorldItem.isItemEqual(stack)) {
// This is the block we are looking for, move along
return PlaceResult.SKIP;
}
}
if (mode == 0 && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
if (mode < 2 && !assumeStackPresent && !s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
new ChatComponentTranslation("structurelib.autoplace.error.no_item_stack", stack.func_151000_E()));
return PlaceResult.REJECT;
}
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
return PlaceResult.REJECT;
if (!s.takeOne(stack, false))
// this is bad! probably an exploit somehow. Let's nullify the block we just placed instead
if (mode < 2) {
if (mode == 1 && !world.isAirBlock(x, y, z)) {
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
// ...and drop it to the player
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
// We either weren't able to give the player the block that was there, or we weren't able to remove
// the block, either way, we need to reject the update. Also, since we hid the block update from
// the client in an effort to make the exchange as seamless as possible, we need to now notify
// the client if we did successfully remove the block.
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
return PlaceResult.REJECT;
}
}
if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
return PlaceResult.REJECT;
}
}
if (mode == 2 && world.isAirBlock(x, y, z)) {
return PlaceResult.SKIP;
}
if (mode == 2 || !s.takeOne(stack, false)) {
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
world.setBlockToAir(x, y, z);
if (mode == 2 && !tryGiveOrDropItem(world, actor, toGive)) {
// We were in removing mode and something went wrong while trying to give the player the block we
// ...removed, let's just fail for now
return PlaceResult.REJECT;
}
}
return PlaceResult.ACCEPT;
}
/**
* Return a structure element that only allow air (or air equivalents, like Railcraft hidden blocks). You usually
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nontheless in case
* you want this as a fallback to something else.
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nonetheless in
* case you want this as a fallback to something else.
*/
@SuppressWarnings("unchecked")
public static <T> IStructureElement<T> isAir() {
@@ -471,8 +577,8 @@ public class StructureUtility {
/**
* Return a structure element that allow anything but air (or air equivalents, like Railcraft hidden blocks). You
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided nontheless
* in case you want this as a fallback to something else.
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided
* nonetheless in case you want this as a fallback to something else.
*/
@SuppressWarnings("unchecked")
public static <T> IStructureElement<T> notAir() {
@@ -638,20 +744,7 @@ public class StructureUtility {
* given tier, then player will be allowed to use multiple kinds of blocks, but their tier is guaranteed to be the
* same.
* <p>
* <h3>WARNING</h3> <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this
* chain of events:
* <ul>
* <li>at check start, tier is reset to notSet
* <li>the tier extractor returns notSet for first block, so since ofBlocksTiered finds your current tier is notSet,
* and the new block's tier is the same as that, so it's accepted
* <li>at 2nd, 3rd..., same thing in 2 happens
* <li>at last occurrence, player placed a valid block with a tier not notSet. ofBlocksTiered now sets your current
* tier to this tier, and accept the block
* <li>check got notified structure is sound, and tier is not notSet. it marks the structure as complete and start
* doing its business
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
* </ul>
* <h3>Example Implementation</h3>
* <b>Example Implementation:</b>
* <p>
* Assume you have 16 tier, each map to one particular block's 16 different meta. You will usually want something
* like this
@@ -678,7 +771,7 @@ public class StructureUtility {
* }
* </pre>
*
* Important bits of this example implementation:
* Important bits:
* <ul>
* <li>The supplied ITierConverter returns null for an invalid block</li>
* <li>Possible tier returned by ITierConverter is Integer object 0 to 15.</li>
@@ -687,8 +780,19 @@ public class StructureUtility {
* <li>notSet is never a tier returned by ITierConverter.</li>
* </ul>
* <p>
* As a friendly reminder, the above characteristics are only for this simply demo, not as a general restriction
* imposed by ofBlocksTiered.
* <p>
* <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this chain of events:
* <ul>
* <li>at check start, tier is reset to notSet
* <li>the tier extractor returns notSet for first block, so since ofBlocksTiered finds your current tier is notSet,
* and the new block's tier is the same as that, so it's accepted
* <li>at 2nd, 3rd..., same thing in 2 happens
* <li>at last occurrence, player placed a valid block with a tier not notSet. ofBlocksTiered now sets your current
* tier to this tier, and accept the block
* <li>check got notified structure is sound, and tier is not notSet. it marks the structure as complete and start
* doing its business
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
* </ul>
*
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
* be passed a null block or an invalid block meta. If this function returns null, then the
@@ -707,23 +811,10 @@ public class StructureUtility {
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<TIER> tierExtractor,
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet, BiConsumer<T, TIER> setter,
Function<T, TIER> getter) {
return ofBlocksTiered(tierExtractor, allKnownTiers, notSet, setter, getter, null);
}
/**
* Like {@link #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)}, but with an explicit
* description that will be returned by {@link IStructureElement#getDescription()}.
*
* @param description the description lang keys to attach to this element, or null for no description
* @see #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)
*/
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<TIER> tierExtractor,
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet, BiConsumer<T, TIER> setter,
Function<T, TIER> getter, @Nullable List<String> description) {
List<Pair<Block, Integer>> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers;
if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException();
IStructureElementCheckOnly<T> check = ofBlocksTiered(tierExtractor, notSet, setter, getter);
return new StructureElement_Bridge<T>() {
return new StructureElement_Bridge<>() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
@@ -780,12 +871,13 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
int mode = ItemConstructableTrigger.getMode(trigger);
Pair<Block, Integer> hint = getHint(trigger);
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
Block block = world.getBlock(x, y, z);
int meta = world.getBlockMetadata(x, y, z);
TIER tier = tierExtractor.convert(block, meta);
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
if (mode < 2 && Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
hint.getKey(),
@@ -798,13 +890,6 @@ public class StructureUtility {
env.getActor(),
env.getChatter());
}
@Nullable
@Override
public List<String> getDescription() {
return description;
}
};
}
@@ -882,7 +967,8 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
if (getBlock() == null) return PlaceResult.REJECT;
return StructureUtility.survivalPlaceBlock(
getBlock(),
@@ -969,7 +1055,8 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
if (init()) return StructureUtility.survivalPlaceBlock(block, meta, world, x, y, z, s, actor, chatter);
return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
@@ -977,7 +1064,8 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
if (init()) return StructureUtility.survivalPlaceBlock(
block,
meta,
@@ -1428,65 +1516,10 @@ public class StructureUtility {
AutoPlaceEnvironment env) {
return BlocksToPlace.create(block, meta);
}
@Nullable
@Override
public List<String> getDescription() {
Item item = Item.getItemFromBlock(block);
if (item == null) return null;
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
}
};
} else {
return new IStructureElement<T>() {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
IItemSource source = env.getSource();
EntityPlayer actor = env.getActor();
Consumer<IChatComponent> chatter = env.getChatter();
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
if (e.getStacks() == null) {
ItemStack taken = source.takeOne(e.getPredicate(), true);
return StructureUtility.survivalPlaceBlock(
taken,
NBTMode.EXACT,
taken.stackTagCompound,
false,
world,
x,
y,
z,
source,
actor,
chatter);
}
for (ItemStack stack : e.getStacks()) {
if (!source.takeOne(stack, true)) {
IChatComponent name = new ChatComponentText(stack.getDisplayName());
name.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW));
env.getChatter()
.accept(new ChatComponentTranslation("structurelib.autoplace.missing_block", name));
continue;
}
return StructureUtility.survivalPlaceBlock(
stack,
NBTMode.EXACT,
stack.stackTagCompound,
false,
world,
x,
y,
z,
source,
actor,
chatter);
}
return PlaceResult.REJECT;
}
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1515,14 +1548,6 @@ public class StructureUtility {
AutoPlaceEnvironment env) {
return BlocksToPlace.create(block, meta);
}
@Nullable
@Override
public List<String> getDescription() {
Item item = Item.getItemFromBlock(block);
if (item == null) return null;
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
}
};
}
}
@@ -1565,14 +1590,6 @@ public class StructureUtility {
// there is getSubItems on ItemBlock, but it's client only
return BlocksToPlace.create(defaultBlock, defaultMeta);
}
@Nullable
@Override
public List<String> getDescription() {
Item item = Item.getItemFromBlock(block);
if (item == null) return null;
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
}
};
} else {
return new IStructureElement<T>() {
@@ -1605,14 +1622,6 @@ public class StructureUtility {
// there is getSubItems on ItemBlock, but it's client only
return BlocksToPlace.create(defaultBlock, defaultMeta);
}
@Nullable
@Override
public List<String> getDescription() {
Item item = Item.getItemFromBlock(block);
if (item == null) return null;
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
}
};
}
}
@@ -1684,7 +1693,9 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && world.getBlock(x, y, z) == defaultBlock
&& world.getBlockMetadata(x, y, z) == defaultMeta)
return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
defaultBlock,
@@ -1730,7 +1741,9 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode < 2 && world.getBlock(x, y, z) == defaultBlock
&& world.getBlockMetadata(x, y, z) == defaultMeta)
return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
defaultBlock,
@@ -1876,71 +1889,6 @@ public class StructureUtility {
AutoPlaceEnvironment env) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return element.getDescription();
}
};
}
/**
* Wraps an element and overrides its description. Useful for providing human-readable names to elements whose
* underlying implementation doesn't provide a description (e.g. tiered block elements).
*
* @param description the description to use, or null to clear any existing description
* @param element the element to wrap
*/
public static <B extends IStructureElement<T>, T> IStructureElement<T> withDescription(
@Nullable List<String> description, B element) {
return new IStructureElement<T>() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return element.check(t, world, x, y, z);
}
@Override
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
return element.couldBeValid(t, world, x, y, z, trigger);
}
@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);
}
@Nullable
@Override
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
return element.getBlocksToPlace(t, world, x, y, z, trigger, env);
}
@Override
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return description;
}
};
}
@@ -1997,12 +1945,6 @@ public class StructureUtility {
AutoPlaceEnvironment env) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return element.getDescription();
}
};
}
@@ -2061,7 +2003,8 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
if (predicate.test(t))
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode == 2 || predicate.test(t))
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
return placeResultWhenDisabled;
}
@@ -2069,15 +2012,11 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
int mode = ItemConstructableTrigger.getMode(trigger);
if (mode == 2 || predicate.test(t))
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
return placeResultWhenDisabled;
}
@Nullable
@Override
public List<String> getDescription() {
return downstream.getDescription();
}
};
}
@@ -2175,12 +2114,6 @@ public class StructureUtility {
AutoPlaceEnvironment env) {
return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return elem.getDescription();
}
};
}
// endregion
@@ -3018,12 +2951,6 @@ public class StructureUtility {
warnNoExplicitSubChannel(env.getActor());
return backing.survivalPlaceBlock(t, world, x, y, z, newTrigger, env);
}
@Nullable
@Override
public List<String> getDescription() {
return backing.getDescription();
}
};
}
// endregion
@@ -3454,4 +3381,35 @@ public class StructureUtility {
}
return shape;
}
/**
* Performs a creation, removal, or update operation at the given position
*
* @param trigger ItemStack for the trigger item
* @param onCreate Operation to be performed when the mode of the trigger is set to building mode
* @param onUpdate Operation to be performed when the mode of the trigger is set to updating mode
* @param onRemove Operation to be performed when the mode of the trigger is set to removing mode
*/
public static void placeRemoveOrUpdate(ItemStack trigger, Runnable onCreate, Runnable onUpdate, Runnable onRemove) {
int mode = ItemConstructableTrigger.getMode(trigger);
switch (mode) {
case 0:
onCreate.run();
break;
case 1:
onUpdate.run();
break;
case 2:
onRemove.run();
break;
}
}
public static boolean tryGiveOrDropItem(World world, EntityPlayer actor, ItemStack stack) {
if (actor.inventory.addItemStackToInventory(stack)) {
actor.inventoryContainer.detectAndSendChanges();
return true;
}
return world.spawnEntityInWorld(new EntityItem(world, actor.posX, actor.posY, actor.posZ, stack));
}
}
@@ -22,6 +22,9 @@ item.structurelib.frontRotationTool.desc.1=Rotates only the front panel,
item.structurelib.frontRotationTool.desc.2=which allows structure rotation.
item.structurelib.constructableTrigger.name=Multiblock Structure Hologram Projector
item.structurelib.constructableTrigger.modes.0=Building Mode
item.structurelib.constructableTrigger.modes.1=Updating Mode
item.structurelib.constructableTrigger.modes.2=Removing Mode
item.structurelib.constructableTrigger.desc.0=Triggers Constructable Interface
item.structurelib.constructableTrigger.desc.1=Shows multiblock construction schematic,
item.structurelib.constructableTrigger.desc.2=use on the multiblock controller.
@@ -30,20 +33,23 @@ item.structurelib.constructableTrigger.desc.4=Quantity of schematics in item sta
item.structurelib.constructableTrigger.desc.5=Hold §l[LSHIFT]§r to display channel data
item.structurelib.constructableTrigger.desc.lshift.0=Contain %d sub channel.
item.structurelib.constructableTrigger.too_fast=Please wait %d milliseconds before another autoplace...
item.structurelib.constructableTrigger.gui.title=Sub Channels
item.structurelib.constructableTrigger.gui.key=Key:
item.structurelib.constructableTrigger.gui.value=Value:
item.structurelib.constructableTrigger.gui.add=Add
item.structurelib.constructableTrigger.gui.set=Update
item.structurelib.constructableTrigger.gui.unset=Remove
item.structurelib.constructableTrigger.gui.wipe=§4Clear
item.structurelib.constructableTrigger.gui.set=Set
item.structurelib.constructableTrigger.gui.unset=Unset
item.structurelib.constructableTrigger.gui.wipe=§4Wipe
item.structurelib.constructableTrigger.gui.error.show=Show Errors
item.structurelib.constructableTrigger.gui.error.hide=Hide Errors
item.structurelib.constructableTrigger.gui.gt_no_hatch.enable=No Hatch
item.structurelib.constructableTrigger.gui.gt_no_hatch.disable=Hatches
item.structurelib.constructableTrigger.keyCategory=Multiblock Structure Hologram Projector Keys
item.structurelib.constructableTrigger.keys.cycleMode=Cycle Projector Mode
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in their structure and a sub channel allows indication of which variant to use.\n All sub channels have a sequence of lowercase characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\n You can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of "§oIndicates§r §ovalue§r §oX§r §ofor§r §osub§r §ochannel§r §oY§r".
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
structurelib.tooltip.channelvalue=§7Indicates value §6%d§r§7 for sub channel §6%s
structurelib.tooltip.indicator_dnd=Drop into GUI to select this value for this channel
structurelib.autoplace.built_stat=Built %s blocks
structurelib.autoplace.complete=§aThis multiblock has completed!
@@ -51,7 +57,6 @@ structurelib.autoplace.error.not_enabled=§cThis multiblock does not support aut
structurelib.autoplace.error.no_simple_block=§cSuggested to place §r%s§c but none was found
structurelib.autoplace.error.no_item_stack=§cSuggested to place block by using §r%s§c but none was found
structurelib.autoplace.warning.no_explicit_channel=§6This structure depends on nonexistent sub channel §r%s§6. Using master channel instead.
structurelib.autoplace.missing_block=§cMissing block: %s
structurelib.blockhint.desc.0=Helps while building
structurelib.blockhint.0.name=Hint 1 dot
@@ -30,7 +30,6 @@ item.structurelib.constructableTrigger.desc.4=此物品堆叠数量会影响显
item.structurelib.constructableTrigger.desc.5=按下 §l[左SHIFT]§r 以显示子信道
item.structurelib.constructableTrigger.desc.lshift.0=包含 %d 个子信道
item.structurelib.constructableTrigger.too_fast=请在下一次自动搭建前等待 %d 毫秒...
item.structurelib.constructableTrigger.gui.title=子信道
item.structurelib.constructableTrigger.gui.key=信道名:
item.structurelib.constructableTrigger.gui.value=值:
item.structurelib.constructableTrigger.gui.add=添加
@@ -38,12 +37,10 @@ item.structurelib.constructableTrigger.gui.set=替换
item.structurelib.constructableTrigger.gui.unset=删除
item.structurelib.constructableTrigger.gui.wipe=§4全部清空
item.structurelib.constructableTrigger.gui.channels.from=§r模组: §9§o%s
item.structurelib.constructableTrigger.gui.info=许多多方块的结构都是可变的,在自动放置时,可以使用子信道来向多方块指明该使用哪一个变体。\n子信道由一个字符串标识、并拥有一个正整数作为值。这个GUI中的信道名支持自动补全到绝大多数已知的子信道。\n你也可以将部分物品从NEI中拖拽至GUI内来设置子信道值为该物品表示的值。这些物品的物品说明中都带有§o表示子信道 X 为 Y§r。
channels.structurelib.show_errors=添加后,在投影时会高亮标出必然错误的部分
structurelib.tooltip.channelvalue=§7表示子信道 §6%2$s§r§7 为 §6%d§r
structurelib.tooltip.indicator_dnd=拖入GUI中来选择该值
structurelib.autoplace.built_stat=已放置 %s 个方块
structurelib.autoplace.complete=§a这个多方块结构已经完成自动搭建!
Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

After

Width:  |  Height:  |  Size: 439 B