mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-09 15:04:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70426ab7b3 | ||
|
|
0030c02e9a | ||
|
|
7382db1bbd | ||
|
|
b669cbecf1 | ||
|
|
6283931667 | ||
|
|
3b3bc4b9ad | ||
|
|
bef4fdd949 | ||
|
|
4a06c8f95b | ||
|
|
241bed1989 | ||
|
|
f5594c25db | ||
|
|
7cda901ae5 | ||
|
|
538459eab5 | ||
|
|
3a9ec8b6ac | ||
|
|
330a13271b | ||
|
|
bf5dd5566b | ||
|
|
b9d0143245 | ||
|
|
43fc39c99b | ||
|
|
6337536dc3 |
No files matched your search
@@ -27,3 +27,4 @@ whitelist.json
|
||||
*.iws
|
||||
src/main/resources/mixins.*.json
|
||||
*.bat
|
||||
layout.json
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.5.19:dev")
|
||||
runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.6.44-GTNH:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.6.39:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.85-GTNH:dev")
|
||||
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ modId = structurelib
|
||||
modGroup = com.gtnewhorizon.structurelib
|
||||
|
||||
# Whether to use modGroup as the maven publishing group.
|
||||
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
|
||||
# When false, com.github.GTNewHorizons is used.
|
||||
useModGroupForPublishing = false
|
||||
|
||||
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
|
||||
@@ -37,7 +37,7 @@ 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 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.
|
||||
@@ -151,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: GTNH Mixins is automatically set as a required dependency if usesMixins = true
|
||||
# Note: UniMixins 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.
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -206,7 +205,7 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.42'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
// this is not an API! go check StructureLibAPI instead!
|
||||
public class ChannelDescription {
|
||||
|
||||
private static final ItemStackMap<Set<ChannelDescription>> itemToChannels = new ItemStackMap<>(true);
|
||||
private static final Map<String, ChannelDescription> registry = new HashMap<>();
|
||||
private final String channel;
|
||||
private final Map<String, String> descriptions = new HashMap<>();
|
||||
private final ItemStackMap<Integer> items = new ItemStackMap<>(true);
|
||||
|
||||
public static ChannelDescription get(final String channel) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||
}
|
||||
|
||||
public static boolean has(final String channel) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return registry.containsKey(channel);
|
||||
}
|
||||
|
||||
public static Collection<Map.Entry<String, Integer>> iterate(final ItemStack block) {
|
||||
Set<ChannelDescription> channels = itemToChannels.get(block);
|
||||
if (channels == null) return Collections.emptySet();
|
||||
return channels.stream().map(d -> new AbstractMap.SimpleImmutableEntry<>(d.channel, d.items.get(block)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static void set(final String channel, final String modid, final String description) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
registry.computeIfAbsent(channel, ChannelDescription::new).add(modid, description);
|
||||
}
|
||||
|
||||
public static void item(final String channel, int channelValue, ItemStack stack) {
|
||||
if (StructureLibAPI.isDebugEnabled()) {
|
||||
if (!channel.toLowerCase(Locale.ROOT).equals(channel)) {
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
}
|
||||
if (channelValue <= 0) {
|
||||
throw new IllegalArgumentException("Channel value must be greater than 0");
|
||||
}
|
||||
}
|
||||
ChannelDescription inst = registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||
inst.items.put(stack, channelValue);
|
||||
itemToChannels.computeIfAbsent(stack, s -> new HashSet<>()).add(inst);
|
||||
}
|
||||
|
||||
public static Map<String, ChannelDescription> getAll() {
|
||||
return Collections.unmodifiableMap(registry);
|
||||
}
|
||||
|
||||
private ChannelDescription(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ChannelDescription add(String modid, String description) {
|
||||
if (Loader.instance().getIndexedModList().get(modid) == null) {
|
||||
throw new IllegalArgumentException("Mod not found: " + modid);
|
||||
}
|
||||
descriptions.put(modid, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getDescriptions() {
|
||||
return Collections.unmodifiableMap(descriptions);
|
||||
}
|
||||
|
||||
public Map<ItemStack, Integer> getItems() {
|
||||
return Collections.unmodifiableMap(items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
|
||||
public class DevelopHelper {
|
||||
|
||||
private static final boolean DEBUG = Boolean.getBoolean("structurelib.develop_helper");
|
||||
|
||||
public static void onPreInit() {
|
||||
if (!DEBUG) return;
|
||||
StructureLib.LOGGER.info("Running develop helper");
|
||||
StructureLibAPI.registerChannelDescription("sandstone", StructureLibAPI.MOD_ID, "variants of Sandstone");
|
||||
StructureLibAPI.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 1, new ItemStack(Blocks.sandstone));
|
||||
StructureLibAPI
|
||||
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 2, new ItemStack(Blocks.sandstone, 1, 1));
|
||||
StructureLibAPI
|
||||
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 3, new ItemStack(Blocks.sandstone, 1, 2));
|
||||
StructureLibAPI.registerChannelDescription("sandbricks", StructureLibAPI.MOD_ID, "variants of Sandbricks");
|
||||
StructureLibAPI.registerChannelDescription("apple", StructureLibAPI.MOD_ID, "apple");
|
||||
StructureLibAPI.registerChannelDescription("bright", StructureLibAPI.MOD_ID, "bright");
|
||||
StructureLibAPI.registerChannelDescription("apple_blight", StructureLibAPI.MOD_ID, "apple_blight");
|
||||
StructureLibAPI.registerChannelDescription("app_eagle", StructureLibAPI.MOD_ID, "app_eagle");
|
||||
}
|
||||
|
||||
public void x() {
|
||||
NBTTagCompound command = new NBTTagCompound();
|
||||
NBTTagList items = new NBTTagList();
|
||||
int[] metas = { 0, 1, 2 };
|
||||
for (int meta : metas) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setTag("Item", new ItemStack(Blocks.sandstone, 1, meta).writeToNBT(new NBTTagCompound()));
|
||||
NBTTagList channels = new NBTTagList();
|
||||
NBTTagCompound channel = new NBTTagCompound();
|
||||
channel.setString("Channel", "sandstone");
|
||||
channel.setInteger("Value", meta + 1);
|
||||
channels.appendTag(channel);
|
||||
tag.setTag("Channels", channels);
|
||||
items.appendTag(tag);
|
||||
}
|
||||
command.setTag("Items", items);
|
||||
FMLInterModComms.sendMessage("structurelib", "register_channel_item", command);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.gui.EmptyContainer;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||
|
||||
import cpw.mods.fml.common.network.IGuiHandler;
|
||||
@@ -11,14 +12,19 @@ public class GuiHandler implements IGuiHandler {
|
||||
|
||||
@Override
|
||||
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||
return null;
|
||||
switch (ID) {
|
||||
case 0:
|
||||
return new EmptyContainer(player);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||
switch (ID) {
|
||||
case 0:
|
||||
return new GuiScreenConfigureChannels(player.inventory.getStackInSlot(x));
|
||||
return new GuiScreenConfigureChannels(new EmptyContainer(player), player.inventory.getStackInSlot(x));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.CHANNEL_SHOW_ERROR;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -24,9 +31,9 @@ import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
@@ -40,7 +47,7 @@ import cpw.mods.fml.relauncher.SideOnly;
|
||||
* This class does not contain a stable API. Refrain from using this class.
|
||||
*/
|
||||
@Mod(
|
||||
modid = StructureLibAPI.MOD_ID,
|
||||
modid = MOD_ID,
|
||||
name = "StructureLib",
|
||||
version = Tags.VERSION,
|
||||
acceptableRemoteVersions = "*",
|
||||
@@ -57,7 +64,7 @@ public class StructureLib {
|
||||
clientSide = "com.gtnewhorizon.structurelib.ClientProxy")
|
||||
static CommonProxy proxy;
|
||||
|
||||
static final SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID);
|
||||
static final SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(MOD_ID);
|
||||
|
||||
static {
|
||||
net.registerMessage(
|
||||
@@ -84,7 +91,7 @@ public class StructureLib {
|
||||
|
||||
@Mod.Instance
|
||||
static StructureLib INSTANCE;
|
||||
|
||||
@Mod.Instance(STRUCTURECOMPAT_MODID)
|
||||
static Object COMPAT;
|
||||
|
||||
static Block blockHint;
|
||||
@@ -115,9 +122,10 @@ public class StructureLib {
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||
|
||||
InventoryUtility.init();
|
||||
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
|
||||
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
|
||||
}
|
||||
|
||||
ChannelDescription.set(CHANNEL_SHOW_ERROR, MOD_ID, "channels.structurelib.show_errors");
|
||||
|
||||
DevelopHelper.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -131,6 +139,48 @@ public class StructureLib {
|
||||
e.registerServerCommand(new CommandRegistryDebug());
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void handleIMC(FMLInterModComms.IMCEvent event) {
|
||||
for (FMLInterModComms.IMCMessage message : event.getMessages()) {
|
||||
switch (message.key) {
|
||||
case "register_channel":
|
||||
processRegisterChannel(message);
|
||||
break;
|
||||
case "register_channel_item":
|
||||
processRegisterChannelItem(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processRegisterChannel(FMLInterModComms.IMCMessage message) {
|
||||
NBTTagList tags = message.getNBTValue().getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||
for (int i = 0; i < tags.tagCount(); i++) {
|
||||
NBTTagCompound tag = tags.getCompoundTagAt(i);
|
||||
StructureLibAPI.registerChannelDescription(
|
||||
tag.getString("Channel"),
|
||||
message.getSender(),
|
||||
tag.getString("Description"));
|
||||
}
|
||||
}
|
||||
|
||||
private void processRegisterChannelItem(FMLInterModComms.IMCMessage message) {
|
||||
NBTTagList items = message.getNBTValue().getTagList("Items", Constants.NBT.TAG_COMPOUND);
|
||||
for (int i = 0; i < items.tagCount(); i++) {
|
||||
NBTTagCompound itemTag = items.getCompoundTagAt(i);
|
||||
ItemStack item = ItemStack.loadItemStackFromNBT(itemTag.getCompoundTag("Item"));
|
||||
NBTTagList tags = itemTag.getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||
for (int j = 0; j < tags.tagCount(); j++) {
|
||||
NBTTagCompound tag = tags.getCompoundTagAt(j);
|
||||
StructureLibAPI.registerChannelItem(
|
||||
tag.getString("Channel"),
|
||||
message.getSender(),
|
||||
tag.getInteger("Value"),
|
||||
item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addClientSideChatMessages(String... messages) {
|
||||
proxy.addClientSideChatMessages(messages);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
@@ -43,6 +44,7 @@ public class StructureLibAPI {
|
||||
public static final int HINT_BLOCK_META_AIR = 13;
|
||||
public static final int HINT_BLOCK_META_NOT_AIR = 14;
|
||||
public static final int HINT_BLOCK_META_ERROR = 15;
|
||||
public static final String CHANNEL_SHOW_ERROR = "show_error";
|
||||
static final ThreadLocal<Object> instrument = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
@@ -72,7 +74,7 @@ public class StructureLibAPI {
|
||||
*/
|
||||
public static void enableInstrument(Object identifier) {
|
||||
if (isInstrumentEnabled()) throw new IllegalStateException();
|
||||
instrument.set(instrument);
|
||||
instrument.set(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,4 +352,37 @@ public class StructureLibAPI {
|
||||
short intervalRequired, boolean forceUpdateLastSend) {
|
||||
proxy.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a channel description here. The description should describe how a channel will be used inside your mod.
|
||||
*
|
||||
* @param channel channel key
|
||||
* @param modid your modid
|
||||
* @param description localization key for your description
|
||||
*/
|
||||
public static void registerChannelDescription(final String channel, final String modid, final String description) {
|
||||
ChannelDescription.set(channel, modid, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an item stack as an indicator for a (channel, value) pair.
|
||||
*
|
||||
* @param channel channel key
|
||||
* @param modid your modid
|
||||
* @param channelValue value
|
||||
* @param stack item stack. having a metadata of
|
||||
* {@link net.minecraftforge.oredict.OreDictionary#WILDCARD_VALUE} will match without comparing
|
||||
* NBT or metadata value. having a tag with {@code tag.getBoolean("*") == true} will match
|
||||
* without comparing NBT.
|
||||
*/
|
||||
public static void registerChannelItem(final String channel, final String modid, final int channelValue,
|
||||
final ItemStack stack) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,28 @@ 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) {
|
||||
|
||||
@@ -1,34 +1,20 @@
|
||||
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 new String[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,4 +36,14 @@ 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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Container;
|
||||
|
||||
public class EmptyContainer extends Container {
|
||||
|
||||
private final EntityPlayer player;
|
||||
|
||||
public EmptyContainer(EntityPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteractWith(EntityPlayer player) {
|
||||
return player == this.player;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,82 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.gtnewhorizon.structurelib.ChannelDescription;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
|
||||
|
||||
public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen {
|
||||
import codechicken.nei.VisiblityData;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.api.TaggedInventoryArea;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = "NotEnoughItems")
|
||||
public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScreen, INEIGuiHandler {
|
||||
|
||||
private static final String I18N_PREFIX = "item.structurelib.constructableTrigger.gui.";
|
||||
|
||||
private static final int ADD_BTN = 0;
|
||||
private static final int UNSET_BTN = 1;
|
||||
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;
|
||||
private final Map<String, List<String>> tooltipSplitCache = new HashMap<>();
|
||||
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);
|
||||
|
||||
public GuiScreenConfigureChannels(ItemStack trigger) {
|
||||
this.trigger = trigger;
|
||||
list = new GuiChannelsList(152, 100, 12, 12, 14);
|
||||
list = new GuiChannelsList(152, 91, 12, 21, 14);
|
||||
list.addSelectionListener((list, selectedIndex) -> {
|
||||
Entry<String, Integer> e = list.getElementAt(selectedIndex);
|
||||
key.setText(e.getKey());
|
||||
value.setText(e.getValue().toString());
|
||||
updateButtons();
|
||||
});
|
||||
autoCompleteList = new GuiAutoCompleteList(151 + 12 - 45 - 2, 50, 45, 119 + 12, 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,33 +162,20 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
};
|
||||
|
||||
list.onGuiInit(this);
|
||||
autoCompleteList.onGuiInit(this);
|
||||
|
||||
addButton(
|
||||
new GuiButton(
|
||||
0,
|
||||
guiLeft + 12,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.add")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
1,
|
||||
guiLeft + 65,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
2,
|
||||
guiLeft + 118,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
|
||||
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")));
|
||||
|
||||
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
|
||||
@@ -199,22 +223,42 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
return buttonList;
|
||||
}
|
||||
|
||||
private boolean isMouseOverValue() {
|
||||
int mx = Mouse.getEventX() * this.width / this.mc.displayWidth;
|
||||
int my = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
|
||||
return mx >= value.xPosition && mx < value.xPosition + value.width
|
||||
&& my >= value.yPosition
|
||||
&& my < value.yPosition + value.height;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMouseInput() {
|
||||
int delta = Mouse.getEventDWheel();
|
||||
if (delta != 0) list.handleDWheel(delta);
|
||||
if (delta != 0) {
|
||||
if (isMouseOverValue()) {
|
||||
if (delta > 0) {
|
||||
value.setText(String.valueOf(getValue() + 1));
|
||||
} else {
|
||||
value.setText(String.valueOf(Math.max(getValue() - 1, 1)));
|
||||
}
|
||||
} else {
|
||||
list.handleDWheel(delta);
|
||||
}
|
||||
}
|
||||
super.handleMouseInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mX, int mY, int button) {
|
||||
super.mouseClicked(mX, mY, button);
|
||||
key.mouseClicked(mX, mY, button);
|
||||
value.mouseClicked(mX, mY, button);
|
||||
super.mouseClicked(mX, mY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char aChar, int aKey) {
|
||||
GuiScrollableList<?> l = key.isFocused() ? autoCompleteList : list;
|
||||
switch (aKey) {
|
||||
case Keyboard.KEY_TAB:
|
||||
if (key.isFocused()) {
|
||||
@@ -227,15 +271,33 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
return;
|
||||
case Keyboard.KEY_RETURN:
|
||||
case Keyboard.KEY_NUMPADENTER:
|
||||
GuiButton add = getButtonList().get(0);
|
||||
if (key.isFocused() && autoCompleteList.getSelectedElement() != null) {
|
||||
key.setText(autoCompleteList.getSelectedElement());
|
||||
key.setFocused(false);
|
||||
value.setFocused(true);
|
||||
value.setText("1");
|
||||
value.setSelectionPos(0);
|
||||
return;
|
||||
}
|
||||
GuiButton add = getButtonList().get(ADD_BTN);
|
||||
if (add.enabled) doActionPerformed(add);
|
||||
return;
|
||||
case Keyboard.KEY_UP:
|
||||
if (list.selectedIndex > 0) list.setSelection(list.selectedIndex - 1);
|
||||
if (l.selectedIndex > 0) l.setSelection(l.selectedIndex - 1);
|
||||
return;
|
||||
case Keyboard.KEY_DOWN:
|
||||
if (list.selectedIndex < list.getNumElements() - 1) list.setSelection(list.selectedIndex + 1);
|
||||
if (l.selectedIndex < l.getNumElements() - 1) l.setSelection(l.selectedIndex + 1);
|
||||
return;
|
||||
case Keyboard.KEY_ESCAPE:
|
||||
if (key.isFocused()) {
|
||||
if (autoCompleteList.getSelectedElement() != null) {
|
||||
autoCompleteList.reset();
|
||||
} else {
|
||||
key.setFocused(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (key.textboxKeyTyped(aChar, aKey)) {
|
||||
updateButtons();
|
||||
@@ -250,10 +312,13 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
// STACKOVERFLOW!
|
||||
String keyText = key.getText();
|
||||
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
|
||||
getButtonList().get(0).displayString = existing ? I18n.format("item.structurelib.constructableTrigger.gui.set")
|
||||
: I18n.format("item.structurelib.constructableTrigger.gui.add");
|
||||
getButtonList().get(0).enabled = !StringUtils.isBlank(value.getText());
|
||||
getButtonList().get(1).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
|
||||
: I18n.format(I18N_PREFIX + "add");
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText())
|
||||
&& Integer.parseInt(value.getText()) > 0;
|
||||
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
|
||||
autoCompleteList.reset();
|
||||
}
|
||||
|
||||
private int getValue() {
|
||||
@@ -268,18 +333,23 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
protected void actionPerformed(GuiButton btn) {
|
||||
if (btn == null) return;
|
||||
switch (btn.id) {
|
||||
case 0:
|
||||
case ADD_BTN:
|
||||
int value = getValue();
|
||||
if (value <= 0) return;
|
||||
ChannelDataAccessor.setChannelData(trigger, key.getText(), value);
|
||||
break;
|
||||
case 1:
|
||||
case UNSET_BTN:
|
||||
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||
break;
|
||||
case 2:
|
||||
case WIPE_BTN:
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
key.setText("");
|
||||
this.value.setText("");
|
||||
break;
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
|
||||
super.actionPerformed(btn);
|
||||
}
|
||||
|
||||
@@ -298,32 +368,234 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesGuiPauseGame() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
public void drawGuiContainerBackgroundLayer(float partialTick, int mX, int mY) {
|
||||
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||
int topLeftX = (this.width - this.getXSize()) / 2;
|
||||
int topLeftY = (this.height - this.getYSize()) / 2;
|
||||
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
|
||||
super.drawScreen(mX, mY, partialTick);
|
||||
list.drawScreen(mX, mY, partialTick);
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.key"),
|
||||
guiLeft + 12,
|
||||
guiTop + 122,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
|
||||
key.drawTextBox();
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.value"),
|
||||
guiLeft + 12,
|
||||
guiTop + 142,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
|
||||
value.drawTextBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
|
||||
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||
autoCompleteList.drawScreen(mouseX, mouseY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
String highlight = null;
|
||||
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||
highlight = autoCompleteList.getSelectedElement();
|
||||
}
|
||||
if (highlight == null && ChannelDescription.has(key.getText())) {
|
||||
highlight = key.getText();
|
||||
}
|
||||
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) {
|
||||
List<String> tooltip = tooltipSplitCache.computeIfAbsent(candidate, this::getChannelDescriptionTooltip);
|
||||
drawHoveringText(tooltip, guiLeft + 151 + 12, guiTop + 122, fontRendererObj);
|
||||
}
|
||||
|
||||
private List<String> getChannelDescriptionTooltip(String candidate) {
|
||||
List<String> tooltip = new ArrayList<>();
|
||||
int maxLine = width - (guiLeft + 151 + 12) - 12;
|
||||
for (Entry<String, String> e : ChannelDescription.get(candidate).getDescriptions().entrySet()) {
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(
|
||||
StatCollector.translateToLocalFormatted(
|
||||
I18N_PREFIX + "channels.from",
|
||||
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
|
||||
maxLine));
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(StatCollector.translateToLocal(e.getValue()), maxLine));
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public String trim(String str, int keyMaxWidth) {
|
||||
Map<String, String> cache = trimCache.get(keyMaxWidth);
|
||||
if (cache == null) {
|
||||
trimCache.put(keyMaxWidth, cache = new HashMap<>());
|
||||
}
|
||||
return cache.computeIfAbsent(str, e -> {
|
||||
String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth);
|
||||
if (s.length() != e.length()) {
|
||||
StringBuilder buffer = new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
|
||||
while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length()
|
||||
!= buffer.length())
|
||||
// drop last original char
|
||||
buffer.deleteCharAt(s.length() - 4);
|
||||
do {
|
||||
buffer.append('.');
|
||||
} while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth);
|
||||
s = buffer.deleteCharAt(s.length() - 1).insert(s.length() - 3, EnumChatFormatting.GRAY).toString();
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Optional.Method(modid = "NotEnoughItems")
|
||||
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
||||
if (button != 0 || mousex <= guiLeft
|
||||
|| mousey <= guiTop
|
||||
|| mousex >= guiLeft + getXSize()
|
||||
|| mousey >= guiTop + getYSize()) {
|
||||
return false;
|
||||
}
|
||||
Collection<Entry<String, Integer>> channels = ChannelDescription.iterate(draggedStack);
|
||||
switch (channels.size()) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
default: // TODO implement a GUI to select which pair to use, instead of blindly using the first
|
||||
Entry<String, Integer> e = channels.iterator().next();
|
||||
ChannelDataAccessor.setChannelData(trigger, e.getKey(), e.getValue());
|
||||
key.setText(e.getKey());
|
||||
value.setText(e.getValue().toString());
|
||||
draggedStack.stackSize = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private class GuiAutoCompleteList extends GuiScrollableList<String> {
|
||||
|
||||
private List<Entry<String, String>> cache; // <key, display string>
|
||||
|
||||
public GuiAutoCompleteList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
super(width, height, originX, originY, slotHeight);
|
||||
margin = 2;
|
||||
}
|
||||
|
||||
private List<Entry<String, String>> getOptions() {
|
||||
if (cache == null) {
|
||||
cache = search();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private List<Entry<String, String>> search() {
|
||||
List<Entry<String, String>> cache = new ArrayList<>();
|
||||
Set<String> added = new HashSet<>();
|
||||
String term = key.getText();
|
||||
String[] subjects = ChannelDescription.getAll().keySet().toArray(new String[0]);
|
||||
Arrays.sort(subjects);
|
||||
if (StringUtils.isBlank(term)) {
|
||||
// nothing. just gives out everything
|
||||
for (String subject : subjects) {
|
||||
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, EnumChatFormatting.GRAY + subject));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
// prefix match
|
||||
for (String s : subjects) {
|
||||
if (s.startsWith(term)) {
|
||||
cache.add(
|
||||
new AbstractMap.SimpleImmutableEntry<>(
|
||||
s,
|
||||
term + EnumChatFormatting.GRAY + s.substring(term.length())));
|
||||
added.add(s);
|
||||
}
|
||||
}
|
||||
// subsequence match
|
||||
for (String subject : subjects) {
|
||||
if (!added.contains(subject) && subject.length() > term.length()) {
|
||||
int i = 0, j = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean mode = true;
|
||||
sb.append(EnumChatFormatting.WHITE);
|
||||
while (i < term.length() && j < subject.length()) {
|
||||
if (term.codePointAt(i) == subject.codePointAt(j)) {
|
||||
if (!mode) {
|
||||
sb.append(EnumChatFormatting.WHITE);
|
||||
mode = true;
|
||||
}
|
||||
sb.appendCodePoint(term.codePointAt(i));
|
||||
i++;
|
||||
j++;
|
||||
} else {
|
||||
if (mode) {
|
||||
sb.append(EnumChatFormatting.GRAY);
|
||||
mode = false;
|
||||
}
|
||||
sb.appendCodePoint(subject.codePointAt(j));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (i == term.length()) {
|
||||
if (j < subject.length()) {
|
||||
sb.append(EnumChatFormatting.GRAY);
|
||||
sb.append(subject, j, subject.length());
|
||||
}
|
||||
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, sb.toString()));
|
||||
added.add(subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
cache = search();
|
||||
selectedIndex = cache.isEmpty() ? -1 : 0;
|
||||
resetScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementAt(int index) {
|
||||
return index >= 0 && index < cache.size() ? getOptions().get(index).getKey() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumElements() {
|
||||
return getOptions().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||
boolean isHovering) {
|
||||
int color = elementIndex == selectedIndex ? 0xffffff : 0xcccccc;
|
||||
fontRendererObj.drawString(getOptions().get(elementIndex).getValue(), x + margin, y + margin / 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> {
|
||||
|
||||
private List<Entry<String, Integer>> cache;
|
||||
@@ -350,6 +622,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
Entry<String, Integer> e = getElementAt(elementIndex);
|
||||
if (e != null) {
|
||||
ChannelDataAccessor.unsetChannelData(trigger, e.getKey());
|
||||
updateButtons();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -390,22 +663,5 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
// args x1, y1, y2, color
|
||||
drawVerticalLine(minX + margin + margin / 2 + KEY_MAX_WIDTH * 2, minY + 1, maxY - 1, 0xffaaaaaa);
|
||||
}
|
||||
|
||||
public String trim(String e, int keyMaxWidth) {
|
||||
// TODO optimize this to not create a billion string, or maybe cache this
|
||||
String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth);
|
||||
if (s.length() != e.length()) {
|
||||
StringBuilder buffer = new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
|
||||
while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length()
|
||||
!= buffer.length())
|
||||
// drop last original char
|
||||
buffer.deleteCharAt(s.length() - 4);
|
||||
do {
|
||||
buffer.append('.');
|
||||
} while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth);
|
||||
s = buffer.deleteCharAt(s.length() - 1).insert(s.length() - 3, EnumChatFormatting.GRAY).toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import org.lwjgl.opengl.GL11;
|
||||
* <p>
|
||||
* As the code has been released into public domain, it is now LGPL as part of this library as a whole. You can go to
|
||||
* EnderCore's repository to obtain a public domain copy though
|
||||
*
|
||||
* <p>
|
||||
* Major edits
|
||||
* <ul>
|
||||
* <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to not fire on
|
||||
@@ -167,10 +167,17 @@ public abstract class GuiScrollableList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetScroll() {
|
||||
amountScrolled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* draws the slot to the screen, pass in mouse's current x and y and partial ticks
|
||||
*/
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glLoadIdentity();
|
||||
GL11.glTranslatef(0.0F, 0.0F, -2000.0F);
|
||||
this.mouseX = mX;
|
||||
this.mouseY = mY;
|
||||
|
||||
@@ -199,13 +206,26 @@ public abstract class GuiScrollableList<T> {
|
||||
int contentYOffset = this.minY + margin - amountScrolled;
|
||||
|
||||
boolean hoveringPrecondition = mX >= minX && mX <= maxX && mY >= minY && mY <= maxY;
|
||||
int slotHeight = this.slotHeight - margin;
|
||||
|
||||
if (selectedIndex != -1) {
|
||||
int elementY = contentYOffset + selectedIndex * this.slotHeight;
|
||||
if (elementY + slotHeight > maxY) {
|
||||
this.amountScrolled = (selectedIndex + 1) * this.slotHeight - this.height + margin;
|
||||
amountScrolled = (int) this.amountScrolled;
|
||||
contentYOffset = this.minY + margin - amountScrolled;
|
||||
} else if (elementY < minY) {
|
||||
this.amountScrolled = selectedIndex * this.slotHeight;
|
||||
amountScrolled = (int) this.amountScrolled;
|
||||
contentYOffset = this.minY + margin - amountScrolled;
|
||||
}
|
||||
}
|
||||
|
||||
prepareDrawElements();
|
||||
|
||||
for (int i = 0; i < getNumElements(); ++i) {
|
||||
|
||||
int elementY = contentYOffset + i * this.slotHeight;
|
||||
int slotHeight = this.slotHeight - margin;
|
||||
|
||||
if (elementY <= maxY && elementY + slotHeight >= minY) {
|
||||
|
||||
@@ -264,6 +284,7 @@ public abstract class GuiScrollableList<T> {
|
||||
GL11.glShadeModel(GL11.GL_FLAT);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
protected void prepareDrawElements() {}
|
||||
@@ -412,7 +433,7 @@ public abstract class GuiScrollableList<T> {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tess.startDrawingQuads();
|
||||
tess.setColorOpaque_I(2105376);
|
||||
tess.setColorOpaque_I(0x202020);
|
||||
tess.addVertex(minX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, minY, 0.0D);
|
||||
|
||||
@@ -28,11 +28,12 @@ public class ItemConstructableTrigger extends Item {
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
} else {
|
||||
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()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
@@ -50,7 +51,7 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
* itself.</li>
|
||||
* <li>The "C" position would be the number of blocks between you and controller, not counting controller itself.</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @param <T> Type of the context object. Usually this will be the multiblock controller.
|
||||
*/
|
||||
public interface IStructureDefinition<T> {
|
||||
@@ -72,7 +73,7 @@ public interface IStructureDefinition<T> {
|
||||
|
||||
/**
|
||||
* Run a structure check.
|
||||
*
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
@@ -107,7 +108,7 @@ public interface IStructureDefinition<T> {
|
||||
|
||||
/**
|
||||
* Spawn hint particles. Should not be called on server side.
|
||||
*
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
@@ -320,7 +321,7 @@ public interface IStructureDefinition<T> {
|
||||
|
||||
/**
|
||||
* Low level utility.
|
||||
*
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param elements the structure piece
|
||||
@@ -394,6 +395,10 @@ public interface IStructureDefinition<T> {
|
||||
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");
|
||||
@@ -420,7 +425,7 @@ public interface IStructureDefinition<T> {
|
||||
|
||||
/**
|
||||
* Create a new instance of builder.
|
||||
*
|
||||
*
|
||||
* @param <T> type of context object
|
||||
*/
|
||||
static <T> StructureDefinition.Builder<T> builder() {
|
||||
|
||||
@@ -32,6 +32,16 @@ public interface IStructureElement<T> {
|
||||
|
||||
boolean check(T t, World world, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Pure (stateless and side effect free) function to test if current block could be valid. Used to give user hints
|
||||
* about incorrectly placed blocks given a controller and trigger item. If couldBeValid(...) == false for a set of
|
||||
* channels, with those same tiers, check(...) == false. Not required, defaults to true which is always safe but
|
||||
* won't give the user error hints.
|
||||
*/
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -185,6 +195,11 @@ public interface IStructureElement<T> {
|
||||
return IStructureElement.this.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return IStructureElement.this.couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
|
||||
@@ -31,6 +31,16 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.couldBeValid(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
|
||||
@@ -14,6 +14,11 @@ public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return false;
|
||||
|
||||
@@ -17,6 +17,11 @@ interface IStructureNavigate<T> extends IStructureElement<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
|
||||
@@ -32,6 +32,11 @@ class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
||||
return get(t).check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return get(t).couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return get(t).placeBlock(t, world, x, y, z, trigger);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
||||
import static java.lang.Integer.MIN_VALUE;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -155,6 +157,11 @@ public class StructureUtility {
|
||||
return world.isAirBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 13);
|
||||
@@ -186,6 +193,11 @@ public class StructureUtility {
|
||||
return !world.isAirBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 14);
|
||||
@@ -225,6 +237,11 @@ public class StructureUtility {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 15);
|
||||
@@ -318,18 +335,24 @@ public class StructureUtility {
|
||||
int itemMeta = itemBlock instanceof ISpecialItemBlock
|
||||
? ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta)
|
||||
: meta;
|
||||
if (!s.takeOne(new ItemStack(itemBlock, 1, itemMeta), false)) {
|
||||
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_simple_block",
|
||||
new ItemStack(itemBlock, 1, itemMeta).func_151000_E()));
|
||||
stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
ICustomBlockSetting block2 = (ICustomBlockSetting) block;
|
||||
block2.setBlock(world, x, y, z, meta);
|
||||
} else {
|
||||
world.setBlock(x, y, z, block, meta, 2);
|
||||
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)) {
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!s.takeOne(stack, false)) {
|
||||
// rollback
|
||||
world.setBlockToAir(x, y, z);
|
||||
}
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
@@ -479,6 +502,11 @@ public class StructureUtility {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), meta);
|
||||
@@ -499,6 +527,11 @@ public class StructureUtility {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, icons.get());
|
||||
@@ -519,6 +552,11 @@ public class StructureUtility {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticleTinted(world, x, y, z, icons.get(), RGBa);
|
||||
@@ -556,7 +594,7 @@ public class StructureUtility {
|
||||
TIER current = getter.apply(t);
|
||||
if (Objects.equals(notSet, current)) {
|
||||
if (Objects.equals(notSet, tier)) {
|
||||
if (StructureLib.PANIC_MODE) {
|
||||
if (PANIC_MODE) {
|
||||
throw new AssertionError("tierExtractor should never return notSet: " + notSet);
|
||||
} else {
|
||||
StructureLib.LOGGER.error("#########################################");
|
||||
@@ -576,35 +614,35 @@ public class StructureUtility {
|
||||
|
||||
/**
|
||||
* Element representing a component with different tiers. Your multi will accept any of them (as long as player does
|
||||
* not mix them (*)), but would like to know which is used. Player can use more blueprint to get hints denoting more
|
||||
* not mix tier (*)), but would like to know which is used. Player can use more blueprint to get hints denoting more
|
||||
* advanced components.
|
||||
* <p>
|
||||
* (*): Implementation wise, this structure element will only accept a block if its tier is the same as existing
|
||||
* tier or if existing tier is unset (i.e. value of third argument)
|
||||
* <p>
|
||||
* Above all else, you need to decide what tier you are going to use. For simpler cases, Integer is a good choice.
|
||||
* You can also use an existing Enum. Tier can never be null though. It can also be a String or a
|
||||
* Above all else, you need to decide what type of tier you are going to use. For simpler cases, Integer is a good
|
||||
* choice. You can also use an existing Enum. It can also be a String or a
|
||||
* {@link net.minecraft.util.ResourceLocation}, or basically anything. It doesn't even have to implement
|
||||
* {@link Comparable}.
|
||||
* <p>
|
||||
* This assumes you will reset the backing storage, and on the first occurrence getter would return notSet. You can
|
||||
* also make getter to return other value to forcefully select a certain tier, but you're probably better off using
|
||||
* block adders or {@link #ofBlock(Block, int, Block, int)} and its overloads
|
||||
* If your tier extractor function returns null on any (Block, meta) pair, then it means this block is rejected.
|
||||
* This also implies null can never be a valid tier.
|
||||
* <p>
|
||||
* Implementation wise, this allows a block only when we don't yet have a tier, or the block at that location has
|
||||
* the same tier as we already have.
|
||||
* <p>
|
||||
* Notice that if your tier extractor function returns null on any (Block, meta) pair, then it means this block is
|
||||
* rejected, since null can never be a valid tier.
|
||||
* This structure element require you will reset the backing storage of getter in your own code, such that in every
|
||||
* round of structure checking, the first call to getter would always return notSet. You can also make getter to
|
||||
* return other value on that occasion to forcefully select a certain tier, but then you'd probably be better off
|
||||
* using block adders or {@link #ofBlock(Block, int, Block, int)} and its overloads
|
||||
* <p>
|
||||
* There is yet no TileEntity counterpart of this utility. Feel free to submit a PR to add it.
|
||||
* <p>
|
||||
* For most typical use cases, you will NOT want to return notSet from your tierExtractor. If you do so, we will
|
||||
* have this kind of chain of events:
|
||||
* (*): Implementation wise, this structure element will only accept a block if its tier is the same as existing
|
||||
* tier or if existing tier is unset (i.e. value of third argument). If there are multiple (block, tier) tuple for a
|
||||
* 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>at first occurrence, ofBlocksTiered finds your current tier is notSet, and the block's tier is notSet, so
|
||||
* it's accepted
|
||||
* <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
|
||||
@@ -612,10 +650,10 @@ public class StructureUtility {
|
||||
* doing its business
|
||||
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
|
||||
* </ul>
|
||||
* <h3>Example Implementation</h3>
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* <p>
|
||||
* Assume you have 16 tier, each map to one block's 16 different meta. You will usually want something like this
|
||||
* Assume you have 16 tier, each map to one particular block's 16 different meta. You will usually want something
|
||||
* like this
|
||||
*
|
||||
* <pre>
|
||||
* public class Tile extends TileEntity {
|
||||
@@ -639,6 +677,18 @@ public class StructureUtility {
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Important bits of this example implementation:
|
||||
* <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>
|
||||
* <li>notSet is set to -1. tier got reset to this value by caller before calling into
|
||||
* {@link IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}</li>
|
||||
* <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.
|
||||
*
|
||||
* @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
|
||||
* block would be considered invalid.
|
||||
@@ -648,8 +698,8 @@ public class StructureUtility {
|
||||
* construction time, use {@link #lazy(Supplier)} or its overloads to delay a bit. This list is
|
||||
* only for hint particle spawning and survival build. The hint/autoplace code will choose 1st
|
||||
* pair to spawn/place if trigger item has master channel data of 1, 2nd pair if 2, and so on.
|
||||
* @param notSet The value returned from {@code getter} when there were no tier info found in T yet. Can be
|
||||
* null.
|
||||
* @param notSet The value returned from {@code getter} when there were no tier info found in T yet. Cannot
|
||||
* be null.
|
||||
* @param getter a function to retrieve the current tier from context object
|
||||
* @param setter a function to set the current tier into context object
|
||||
*/
|
||||
@@ -667,9 +717,23 @@ public class StructureUtility {
|
||||
}
|
||||
|
||||
private Pair<Block, Integer> getHint(ItemStack trigger) {
|
||||
if (hints.isEmpty()) return null;
|
||||
return hints.get(Math.min(Math.max(trigger.stackSize, 1), hints.size()) - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
if (hint == null) return true;
|
||||
TIER hintTier = tierExtractor.convert(hint.getKey(), hint.getValue());
|
||||
Block block = world.getBlock(x, y, z);
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
TIER worldTier = tierExtractor.convert(block, meta);
|
||||
|
||||
// if the block is in the same tier as the hint block, this could be valid
|
||||
return Objects.equals(hintTier, worldTier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
@@ -767,6 +831,11 @@ public class StructureUtility {
|
||||
return world.getBlock(x, y, z) == getBlock() && (wildcard || world.getBlockMetadata(x, y, z) == meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
if (getBlock() == null) return error().spawnHint(t, world, x, y, z, trigger);
|
||||
@@ -841,10 +910,16 @@ public class StructureUtility {
|
||||
|
||||
@Override
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
if (init()) return world.getBlock(x, y, z) != block && world.getBlockMetadata(x, y, z) == meta;
|
||||
if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta;
|
||||
else return fallback.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta;
|
||||
else return fallback.couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
if (init()) {
|
||||
@@ -921,6 +996,11 @@ public class StructureUtility {
|
||||
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -958,6 +1038,11 @@ public class StructureUtility {
|
||||
.contains(worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -982,6 +1067,11 @@ public class StructureUtility {
|
||||
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -1016,6 +1106,13 @@ public class StructureUtility {
|
||||
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// calling iBlockAdder can potentially modify external state
|
||||
// therefore we assume this can always be valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -1049,6 +1146,11 @@ public class StructureUtility {
|
||||
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1093,6 +1195,11 @@ public class StructureUtility {
|
||||
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1158,6 +1265,11 @@ public class StructureUtility {
|
||||
.contains(worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1203,6 +1315,11 @@ public class StructureUtility {
|
||||
.contains(worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1251,6 +1368,14 @@ public class StructureUtility {
|
||||
if (block == null || defaultBlock == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (block == Blocks.air) {
|
||||
if (PANIC_MODE) {
|
||||
throw new IllegalArgumentException("ofBlock() does not accept air. use isAir() instead");
|
||||
} else {
|
||||
LOGGER.warn("ofBlock() does not accept air. use isAir() instead");
|
||||
return isAir();
|
||||
}
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
return new IStructureElement<T>() {
|
||||
|
||||
@@ -1260,6 +1385,11 @@ public class StructureUtility {
|
||||
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1287,6 +1417,11 @@ public class StructureUtility {
|
||||
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1323,6 +1458,11 @@ public class StructureUtility {
|
||||
return block == world.getBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1350,6 +1490,11 @@ public class StructureUtility {
|
||||
return block == world.getBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return check(t, 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);
|
||||
@@ -1416,6 +1561,13 @@ public class StructureUtility {
|
||||
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// calling iBlockAdder can potentially modify external state
|
||||
// therefore we assume this can always be valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
|
||||
@@ -1455,6 +1607,13 @@ public class StructureUtility {
|
||||
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// calling iBlockAdder can potentially modify external state
|
||||
// therefore we assume this can always be valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
|
||||
@@ -1510,6 +1669,13 @@ public class StructureUtility {
|
||||
return iTileAdder.apply(t, tileEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// calling iTileAdder can potentially modify external state
|
||||
// therefore we assume this can always be valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -1536,6 +1702,13 @@ public class StructureUtility {
|
||||
return tileClass.isInstance(tileEntity) && iTileAdder.test(t, tileClass.cast(tileEntity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// calling iTileAdder can potentially modify external state
|
||||
// therefore we assume this can always be valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
||||
@@ -1569,6 +1742,11 @@ public class StructureUtility {
|
||||
return check;
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -1620,6 +1798,11 @@ public class StructureUtility {
|
||||
return check;
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -1678,6 +1861,13 @@ public class StructureUtility {
|
||||
return predicate.test(t) && downstream.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
// this COULD potentially impact external state
|
||||
// but predicate really should not have side effects
|
||||
return predicate.test(t) && downstream.couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return predicate.test(t) && downstream.spawnHint(t, world, x, y, z, trigger);
|
||||
@@ -1774,6 +1964,11 @@ public class StructureUtility {
|
||||
return elem.check(t.getCurrentContext(), world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return elem.couldBeValid(t.getCurrentContext(), world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return elem.spawnHint(t.getCurrentContext(), world, x, y, z, trigger);
|
||||
@@ -1861,6 +2056,11 @@ public class StructureUtility {
|
||||
return to.get().check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return to.get().couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -1914,6 +2114,11 @@ public class StructureUtility {
|
||||
return to.apply(t).check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return to.apply(t).couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -2126,6 +2331,11 @@ public class StructureUtility {
|
||||
return to.apply(t, null).check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -2318,6 +2528,11 @@ public class StructureUtility {
|
||||
return toCheck.apply(t).check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -2562,6 +2777,11 @@ public class StructureUtility {
|
||||
return backing.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
||||
return backing.couldBeValid(t, world, x, y, z, newTrigger);
|
||||
}
|
||||
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
||||
// We know spawnHint will only be called on client side, so the lack of player isn't quite an issue
|
||||
|
||||
@@ -178,6 +178,80 @@ StructureLib API documentation.
|
||||
|
||||
Just give me a better name and I'd do the renaming.
|
||||
|
||||
<h3>Channel Descriptions</h3>
|
||||
<p>
|
||||
Each channel can have one description associated with it per mod.
|
||||
Channel description should briefly describe how a specific channel is used for inside a specific mod.
|
||||
They will be displayed to player in channel editor GUI that comes with StructureLib.
|
||||
The registration process can be done either via direct method call to {@link com.gtnewhorizon.structurelib.StructureLibAPI#registerChannelDescription(String, String, String)},
|
||||
or by sending an IMC message.
|
||||
After registration, the channel will also show up as autocomplete options when players are editing channels.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Here is an example of sending an IMC message to register two channels and their description.
|
||||
In this example, we assume your modid is <code>my_mod</code> and your mod will ship with localization entries for
|
||||
localization key <code>channels.my_mod.channel_1</code> and <code>channels.my_mod.channel_2</code>
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
NBTTagCompound command = new NBTTagCompound();
|
||||
NBTTagList tags = new NBTTagList();
|
||||
String[] channels = {"channel_1", "channel_2"};
|
||||
for (String channel : channels) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setString("Channel", channel);
|
||||
tag.setString("Description", "channels.my_mod." + channel);
|
||||
tags.appendTag(tag);
|
||||
}
|
||||
command.setTag("Channels", tags);
|
||||
FMLInterModComms.sendMessage("structurelib", "register_channel", command);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
There is no strict rules over whether you should do direct API method call or send IMC messages.
|
||||
You can choose whichever you prefer.
|
||||
</p>
|
||||
|
||||
<h3>Channel Indicator Items</h3>
|
||||
<p>
|
||||
You can associate an ItemStack with a given (channel, value) pair.
|
||||
It is usually (but not required to be) the ItemBlock for the blocks to be placed by this (channel, value) pair.
|
||||
Player will be able to drag and drop this item to the builtin channel configuration GUI.
|
||||
The registration process can be done either via direct method call to {@link com.gtnewhorizon.structurelib.StructureLibAPI#registerChannelItem(String, String, int, net.minecraft.item.ItemStack)},
|
||||
or by sending an IMC message.
|
||||
After registration, the channel will also show up as autocomplete options when players are editing channels.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Here is an example of sending an IMC message to register 3 vanilla sandstone as different indicator for channel "sandstone", with block metadata plus 1 as channel value.
|
||||
In this example, we assume your modid is <code>my_mod</code>.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
NBTTagCompound command = new NBTTagCompound();
|
||||
NBTTagList items = new NBTTagList();
|
||||
int[] metas = {0, 1, 2};
|
||||
for (int meta : metas) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setTag("Item", new ItemStack(Blocks.sandstone, 1, meta).writeToNBT(new NBTTagCompound()));
|
||||
NBTTagList channels = new NBTTagList();
|
||||
NBTTagCompound channel = new NBTTagCompound();
|
||||
channel.setString("Channel", "sandstone");
|
||||
channel.setInteger("Value", meta + 1);
|
||||
channels.appendTag(channel);
|
||||
tag.setTag("Channels", channels);
|
||||
items.appendTag(tag);
|
||||
}
|
||||
command.setTag("Items", items);
|
||||
FMLInterModComms.sendMessage("structurelib", "register_channel_item", command);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
There is no strict rules over whether you should do direct API method call or send IMC messages.
|
||||
You can choose whichever you prefer.
|
||||
</p>
|
||||
|
||||
<h3>Examples</h3>
|
||||
|
||||
<h4>For Structure Definition Author</h4>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
tile.structurelib.blockhint.name=Hint
|
||||
tile.structurelib.blockhint.0.name=Hint 1 dot
|
||||
tile.structurelib.blockhint.1.name=Hint 2 dots
|
||||
tile.structurelib.blockhint.2.name=Hint 3 dots
|
||||
@@ -29,12 +30,20 @@ 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=Set
|
||||
item.structurelib.constructableTrigger.gui.unset=Unset
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Wipe
|
||||
item.structurelib.constructableTrigger.gui.set=Update
|
||||
item.structurelib.constructableTrigger.gui.unset=Remove
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Clear
|
||||
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!
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
tile.structurelib.blockhint.name=提示方块
|
||||
tile.structurelib.blockhint.0.name=提示方块:1号
|
||||
tile.structurelib.blockhint.1.name=提示方块:2号
|
||||
tile.structurelib.blockhint.2.name=提示方块:3号
|
||||
@@ -29,12 +30,20 @@ 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=添加
|
||||
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: 6.6 KiB After Width: | Height: | Size: 1.3 KiB |
Reference in new issue
Block a user