mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-10-26 14:23:56 +00:00
channels
This commit is contained in:
18 files changed
+1404
-15
No files matched your search
+8
-2
@@ -57,7 +57,7 @@ plugins {
|
||||
id 'com.palantir.git-version' version '0.13.0' apply false
|
||||
id 'de.undercouch.download' version '5.0.1'
|
||||
id 'com.github.gmazzo.buildconfig' version '3.0.3' apply false
|
||||
id "com.diffplug.spotless" version "6.7.2"
|
||||
id 'com.diffplug.spotless' version '6.7.2' apply false
|
||||
}
|
||||
verifySettingsGradle()
|
||||
|
||||
@@ -90,7 +90,13 @@ idea {
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
apply from: Blowdryer.file('spotless.gradle')
|
||||
|
||||
boolean disableSpotless = project.hasProperty("disableSpotless") ? project.disableSpotless.toBoolean() : false
|
||||
|
||||
if (!disableSpotless) {
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
apply from: Blowdryer.file('spotless.gradle')
|
||||
}
|
||||
|
||||
if(JavaVersion.current() != JavaVersion.VERSION_1_8) {
|
||||
throw new GradleException("This project requires Java 8, but it's running on " + JavaVersion.current())
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
dependencies {
|
||||
compile("com.github.GTNewHorizons:GTNHLib:0.0.1:dev")
|
||||
runtime("com.github.GTNewHorizons:NotEnoughItems:2.2.20-GTNH:dev")
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
# Constructable Tool
|
||||
|
||||
The convention is to use the stack size of tool stack to pass tier info.
|
||||
|
||||
## Channels
|
||||
|
||||
When one multi have multiple tiered component that can have interleaving tier, this becomes an obnoxious restriction, as
|
||||
one ItemStack cannot have two stack sizes at the same time.
|
||||
Here we use the item NBT to bypass this restriction.
|
||||
|
||||
Basically, one hologram can contain multiple channels.
|
||||
Each channel can be identified with lower case strings and has the value being one positive Java int.
|
||||
There will be one and only one master channel and 0 or more sub channel.
|
||||
|
||||
When a non-existent sub channel is queried, master channel will be used instead to fill its place.
|
||||
|
||||
Structure element designers will usually not need to consider channels, i.e. proceed as if this concept does not exist, unless it must take input from multiple channels.
|
||||
|
||||
Multi Designers will consider how to map each channel to each tiered component.
|
||||
He will be using `com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel` to direct structure element on which channel to use.
|
||||
Conceptually, it will return an ItemStack with the master channel being the value of selected channel.
|
||||
In implementation, withChannel will return a new ItemStack with same item, nbt but different stack size.
|
||||
|
||||
In any case, it is NOT safe to mutate or query this ItemStack beyond its stack size.
|
||||
What information this carries (beyond the stack size) is an implementation detail and should not be relied on.
|
||||
__For the future of mankind please do not rely on this.__
|
||||
|
||||
### why is this called channel?
|
||||
|
||||
IDK man, just give me a better name and I'd do the renaming.
|
||||
|
||||
### Example: Multiblock Structure Designer
|
||||
|
||||
GT++ Chemical Plant is a multi with 4 distinctly tiered component.
|
||||
|
||||
It will use
|
||||
* master channel for its outer shell
|
||||
* channel `"casing"` for machine casings
|
||||
* channel `"pipe"` for pipe casings
|
||||
* channel `"coil"` for heating coils
|
||||
|
||||
Its structure code will then look like
|
||||
```java
|
||||
.addElement('a', shellStructureElement())
|
||||
.addElement('b', withChannel("casing", machineCasingStructureElement()))
|
||||
.addElement('c', withChannel("pipe", pipeCasingStructureElement()))
|
||||
.addElement('d', withChannel("coil", coilStructureElement()))
|
||||
```
|
||||
|
||||
whereas all these structure elements are designed without considering channels whatsoever
|
||||
|
||||
### Example: Multi channel input Structure Element
|
||||
|
||||
Suppose your hatch element has a required facing and can accept arbitrary meta tile entity, so it will need both meta id and facing input.
|
||||
|
||||
|
||||
It will use
|
||||
* master channel for its outer shell
|
||||
* channel `"casing"` for machine casings
|
||||
* channel `"pipe"` for pipe casings
|
||||
* channel `"coil"` for heating coils
|
||||
|
||||
Its structure code will then look like
|
||||
```java
|
||||
.addElement('a', shellStructureElement())
|
||||
.addElement('b', withChannel("casing", machineCasingStructureElement()))
|
||||
.addElement('c', withChannel("pipe", pipeCasingStructureElement()))
|
||||
.addElement('d', withChannel("coil", coilStructureElement()))
|
||||
```
|
||||
|
||||
whereas all these structure elements are designed without considering channels whatsoever
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# StructureLib Developer Guide v0.1-SNAPSHOT
|
||||
|
||||
## `IStructureDefinition`
|
||||
|
||||
`IStructureDefinition` is the structure definition of your multi. You will have one `IStructureDefinition` for each multi.
|
||||
|
||||
### Caching?
|
||||
|
||||
You will construct this definition once per multi, either at class load or at first call to `getStructureDefinition()`.
|
||||
You can use a `ClassValue<IStructureDefinition<?>>` if you have a template for subclass.
|
||||
You can also use `StructureUtility.defer()` for simpler cases, but it is slightly less performant as it will allocate new
|
||||
objects on each structure check, whereas `ClassValue` will allocate once and cache it onwards.
|
||||
|
||||
### Constructing
|
||||
|
||||
IStructureDefinition is constructed using `IStructureDefinition#builder()`.
|
||||
|
||||
Builder has two main methods
|
||||
1. `addShape` is a 2d string array describing the layout of structure. You can optionally use `StructureUtility#transpose()`
|
||||
to transpose the array to make the structure look nicer in source code.
|
||||
2. `addElement`. It takes a char identifier (mapped to a char in any of `addShape()` calls) and a IStructureElement.
|
||||
|
||||
## `IStructureElement`
|
||||
|
||||
`IStructureElement` are how you define which block will be accepted.
|
||||
They are simple stateless classes. They will rely on the multiblock controller to store any states they need.
|
||||
If you don't want to store these transient states in multiblock controller, you can use `StructureUtility#withContext()`
|
||||
|
||||
They will usually come from `StructureUtility`.
|
||||
There is a vast library of `IStructureElement` there to cover basic needs.
|
||||
For more complex usage, you can define your own subclass, e.g. GregTech 5 Unofficial defines `ofHatchAdder()`, `ofCoil()`,
|
||||
`ofFrame()` and `buildHatchAdder()` in its `GT_StructureUtility` class.
|
||||
|
||||
Here we will detail a few more complex `IStructureElement` that comes with standard library.
|
||||
|
||||
### `ofBlocksTiered`
|
||||
|
||||
This assumes you have a series of block, e.g. T1 motor, T2 motor, etc. Your multi will accept any of them (__as long as
|
||||
player does not mix them (*)__), but would like to know which is used.
|
||||
|
||||
(*): 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)
|
||||
|
||||
The first argument is tier extractor.
|
||||
You will need to provide a function to convert block+meta to a tier.
|
||||
This tier cam be an Enum, an Integer, a String, or basically anything.
|
||||
It doesn't even have to implement `Comparable`.
|
||||
The choice is yours.
|
||||
The extractor can return null and will never be passed a null block or an invalid block meta.
|
||||
|
||||
The second argument is a list of all blocks you know that you will accept.
|
||||
This list is only for hint particle spawning and survival build.
|
||||
The hint/autoplace code will choose 1st pair to spawn/place if hologram has [channel data](./channels.md) of 1,
|
||||
2nd pair if 2, and so on.
|
||||
|
||||
The third argument is the value when the tier has not been set by structure code.
|
||||
This means getter (5th argument) will usually return this upon first invocation for each round of structure check.
|
||||
|
||||
The fourth argument will notify the multi of a tier change.
|
||||
|
||||
The fifth argument is how multi notify the structure element about current tier.
|
||||
This is necessary as all structure element are stateless themselves.
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.gtnewhorizon.structurelib.entity.fx.EntityFXBlockHint;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import java.util.ArrayList;
|
||||
@@ -14,6 +15,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiNewChat;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
@@ -149,6 +151,11 @@ public class ClientProxy extends CommonProxy {
|
||||
return Minecraft.getMinecraft().theWorld.getTotalWorldTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadChannels(ItemStack trigger) {
|
||||
StructureLib.net.sendToServer(new SetChannelDataMessage(trigger));
|
||||
}
|
||||
|
||||
private static class HintParticleInfo {
|
||||
private final int x, y, z;
|
||||
private final List<EntityFXBlockHint> owner;
|
||||
|
||||
@@ -5,6 +5,7 @@ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
@@ -48,4 +49,6 @@ public class CommonProxy {
|
||||
public long getOverworldTime() {
|
||||
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||
}
|
||||
|
||||
public void uploadChannels(ItemStack trigger) {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||
import cpw.mods.fml.common.network.IGuiHandler;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class GuiHandler implements IGuiHandler {
|
||||
@Override
|
||||
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||
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));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import com.gtnewhorizon.structurelib.block.BlockHint;
|
||||
import com.gtnewhorizon.structurelib.command.CommandConfigureChannels;
|
||||
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
|
||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
@@ -49,10 +52,14 @@ public class StructureLib {
|
||||
AlignmentMessage.ServerHandler.class, AlignmentMessage.AlignmentQuery.class, 0, Side.SERVER);
|
||||
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
|
||||
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
|
||||
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
|
||||
}
|
||||
|
||||
public static final XSTR RANDOM = new XSTR();
|
||||
|
||||
@Mod.Instance
|
||||
static StructureLib INSTANCE;
|
||||
|
||||
static Block blockHint;
|
||||
static Item itemBlockHint;
|
||||
static Item itemFrontRotationTool;
|
||||
@@ -76,6 +83,12 @@ public class StructureLib {
|
||||
itemConstructableTrigger = new ItemConstructableTrigger(),
|
||||
itemConstructableTrigger.getUnlocalizedName());
|
||||
proxy.preInit(e);
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void serverStarting(FMLServerStartingEvent e) {
|
||||
e.registerServerCommand(new CommandConfigureChannels());
|
||||
}
|
||||
|
||||
public static void addClientSideChatMessages(String... messages) {
|
||||
@@ -93,4 +106,12 @@ public class StructureLib {
|
||||
public static long getOverworldTime() {
|
||||
return proxy.getOverworldTime();
|
||||
}
|
||||
|
||||
public static StructureLib instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public CommonProxy proxy() {
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
|
||||
import static com.gtnewhorizon.structurelib.util.MiscUtils.getTagKeys;
|
||||
import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND;
|
||||
|
||||
public class ChannelDataAccessor {
|
||||
private static final String SECONDARY_HINT_TAG = "channels";
|
||||
|
||||
private ChannelDataAccessor() {}
|
||||
|
||||
public static ItemStack withChannel(ItemStack masterStack, String channel) {
|
||||
if (channel == null || channel.isEmpty()) throw new IllegalArgumentException();
|
||||
if (StructureLib.DEBUG_MODE && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (!masterStack.hasTagCompound()
|
||||
|| !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
|| !masterStack
|
||||
.stackTagCompound
|
||||
.getCompoundTag(SECONDARY_HINT_TAG)
|
||||
.hasKey(channel, NBT.TAG_INT)) return masterStack;
|
||||
ItemStack ret = new ItemStack(
|
||||
masterStack.getItem(),
|
||||
masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel),
|
||||
Items.feather.getDamage(masterStack));
|
||||
ret.setTagCompound(masterStack.stackTagCompound);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean hasSubChannel(ItemStack masterStack) {
|
||||
return masterStack.hasTagCompound() && masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND);
|
||||
}
|
||||
|
||||
public static boolean hasSubChannel(ItemStack masterStack, String channel) {
|
||||
if (StructureLib.DEBUG_MODE && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return !channel.isEmpty()
|
||||
&& masterStack.hasTagCompound()
|
||||
&& masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
&& masterStack
|
||||
.stackTagCompound
|
||||
.getCompoundTag(SECONDARY_HINT_TAG)
|
||||
.hasKey(channel, NBT.TAG_INT);
|
||||
}
|
||||
|
||||
public static int getChannelData(ItemStack masterStack, String channel) {
|
||||
if (channel == null || channel.isEmpty()) throw new IllegalArgumentException();
|
||||
if (StructureLib.DEBUG_MODE && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (!masterStack.hasTagCompound()
|
||||
|| !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
|| !masterStack
|
||||
.stackTagCompound
|
||||
.getCompoundTag(SECONDARY_HINT_TAG)
|
||||
.hasKey(channel, NBT.TAG_INT)) return masterStack.stackSize;
|
||||
return masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel);
|
||||
}
|
||||
|
||||
public static void setChannelData(ItemStack masterStack, String channel, int data) {
|
||||
if (channel == null || channel.isEmpty()) throw new IllegalArgumentException();
|
||||
if (StructureLib.DEBUG_MODE && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (data <= 0) throw new IllegalArgumentException();
|
||||
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
|
||||
NBTTagCompound main = masterStack.stackTagCompound;
|
||||
if (!main.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)) main.setTag(SECONDARY_HINT_TAG, new NBTTagCompound());
|
||||
main.getCompoundTag(SECONDARY_HINT_TAG).setInteger(channel, data);
|
||||
}
|
||||
|
||||
public static void unsetChannelData(ItemStack masterStack, String channel) {
|
||||
if (channel == null || channel.isEmpty()) throw new IllegalArgumentException();
|
||||
if (StructureLib.DEBUG_MODE && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
|
||||
NBTTagCompound main = masterStack.stackTagCompound;
|
||||
if (!main.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)) main.setTag(SECONDARY_HINT_TAG, new NBTTagCompound());
|
||||
NBTTagCompound tag = main.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
tag.removeTag(channel);
|
||||
if (tag.hasNoTags()) main.removeTag(SECONDARY_HINT_TAG);
|
||||
if (main.hasNoTags()) masterStack.stackTagCompound = null;
|
||||
}
|
||||
|
||||
public static void wipeChannelData(ItemStack masterStack) {
|
||||
if (masterStack.stackTagCompound != null) masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
||||
}
|
||||
|
||||
public static Stream<Entry<String, Integer>> iterateChannelData(ItemStack masterStack) {
|
||||
if (!hasSubChannel(masterStack)) return Stream.empty();
|
||||
NBTTagCompound tag = masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
return getTagKeys(tag).stream().map(s -> new ImmutablePair<>(s, tag.getInteger(s)));
|
||||
}
|
||||
|
||||
public static int countChannelData(ItemStack masterStack) {
|
||||
if (!hasSubChannel(masterStack)) return 0;
|
||||
NBTTagCompound tag = masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
return tag.func_150296_c().size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.gtnewhorizon.structurelib.command;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.command.WrongUsageException;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
public class CommandConfigureChannels extends CommandBase {
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "sl_channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender p_71518_1_) {
|
||||
return "/sl_channel <wipe/getall>\n" + "/sl_channel <get/unset> <channel name>\n"
|
||||
+ "/sl_channel <set> <channel name> <value>\n"
|
||||
+ "<channel name> can have space in it. <value> must be positive integer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequiredPermissionLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<?> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
|
||||
if (p_71516_2_.length == 1)
|
||||
return getListOfStringsMatchingLastWord(p_71516_2_, "get", "set", "unset", "wipe", "getall");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {
|
||||
if (args.length == 0) throw new WrongUsageException(getCommandUsage(sender));
|
||||
|
||||
if (!(sender instanceof EntityPlayerMP)) throw new WrongUsageException("must be a player");
|
||||
|
||||
EntityPlayerMP player = (EntityPlayerMP) sender;
|
||||
|
||||
ItemStack heldItem = player.getHeldItem();
|
||||
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) {
|
||||
throw new WrongUsageException("must hold a hologram");
|
||||
}
|
||||
|
||||
String channel;
|
||||
|
||||
switch (args[0]) {
|
||||
case "getall":
|
||||
if (!ChannelDataAccessor.hasSubChannel(heldItem)) {
|
||||
sender.addChatMessage(new ChatComponentText("No subchannel"));
|
||||
} else {
|
||||
sender.addChatMessage(new ChatComponentTranslation(
|
||||
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||
ChannelDataAccessor.countChannelData(heldItem)));
|
||||
ChannelDataAccessor.iterateChannelData(heldItem)
|
||||
.map(e -> e.getKey() + ": " + e.getValue())
|
||||
.map(ChatComponentText::new)
|
||||
.forEach(player::addChatMessage);
|
||||
}
|
||||
return;
|
||||
case "wipe":
|
||||
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||
return;
|
||||
case "get":
|
||||
case "unset":
|
||||
if (args.length < 2) throw new WrongUsageException(getCommandUsage(sender));
|
||||
channel =
|
||||
args.length == 2 ? args[1] : Arrays.stream(args).skip(1).collect(Collectors.joining());
|
||||
break;
|
||||
case "set":
|
||||
if (args.length < 3) throw new WrongUsageException(getCommandUsage(sender));
|
||||
channel = args.length == 3
|
||||
? args[1]
|
||||
: Arrays.stream(args).skip(1).limit(args.length - 2).collect(Collectors.joining());
|
||||
break;
|
||||
default:
|
||||
throw new WrongUsageException(getCommandUsage(sender));
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
case "get":
|
||||
if (ChannelDataAccessor.hasSubChannel(heldItem, channel))
|
||||
sender.addChatMessage(new ChatComponentText(
|
||||
channel + " value: " + ChannelDataAccessor.getChannelData(heldItem, channel)));
|
||||
else sender.addChatMessage(new ChatComponentText(channel + " value: N/A"));
|
||||
break;
|
||||
case "clear":
|
||||
if (ChannelDataAccessor.hasSubChannel(heldItem, channel))
|
||||
sender.addChatMessage(new ChatComponentText(channel + " no value"));
|
||||
else {
|
||||
ChannelDataAccessor.unsetChannelData(heldItem, channel);
|
||||
sender.addChatMessage(new ChatComponentText(channel + " cleared"));
|
||||
player.inventoryContainer.detectAndSendChanges();
|
||||
}
|
||||
break;
|
||||
case "set":
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(args[args.length - 1]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new WrongUsageException("%s not a valid value", args[args.length - 1]);
|
||||
}
|
||||
|
||||
if (value <= 0) throw new WrongUsageException("%s not a valid value", args[args.length - 1]);
|
||||
|
||||
ChannelDataAccessor.setChannelData(heldItem, channel, value);
|
||||
player.inventoryContainer.detectAndSendChanges();
|
||||
sender.addChatMessage(new ChatComponentText(channel + " value: " + value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen {
|
||||
private static final int KEY_MAX_WIDTH = 50;
|
||||
private final ItemStack trigger;
|
||||
private final GuiChannelsList list;
|
||||
private GuiTextField key, value;
|
||||
protected int guiTop, guiLeft;
|
||||
|
||||
public GuiScreenConfigureChannels(ItemStack trigger) {
|
||||
this.trigger = trigger;
|
||||
list = new GuiChannelsList(152, 100, 12, 12, 14);
|
||||
list.addSelectionListener((list, selectedIndex) -> {
|
||||
Entry<String, Integer> e = list.getElementAt(selectedIndex);
|
||||
key.setText(e.getKey());
|
||||
value.setText(e.getValue().toString());
|
||||
updateButtons();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
super.initGui();
|
||||
// so you can keep holding backspace to delete a long sequence of chars
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
guiLeft = (this.width - this.getXSize()) / 2;
|
||||
guiTop = (this.height - this.getYSize()) / 2;
|
||||
|
||||
key = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 119, 151 + 12 - 45, 12) {
|
||||
@Override
|
||||
public void writeText(String text) {
|
||||
// force lower case
|
||||
super.writeText(text.toLowerCase(Locale.ROOT));
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||
boolean flag = p_146192_1_ >= this.xPosition
|
||||
&& p_146192_1_ < this.xPosition + this.width
|
||||
&& p_146192_2_ >= this.yPosition
|
||||
&& p_146192_2_ < this.yPosition + this.height;
|
||||
if (flag && p_146192_3_ == 1) {
|
||||
key.setText("");
|
||||
value.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String p_146180_1_) {
|
||||
super.setText(p_146180_1_);
|
||||
updateButtons();
|
||||
}
|
||||
};
|
||||
value = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 139, 151 + 12 - 45, 12) {
|
||||
@Override
|
||||
public void writeText(String text) {
|
||||
// ignore write requests containing non digit characters.
|
||||
// we don't accept decimals or negative numbers, so we can safely use isDigit instead of something more
|
||||
// advanced
|
||||
// use codePoints() in case someone pasted an emoji by mistake
|
||||
if (text != null && text.codePoints().allMatch(Character::isDigit)) {
|
||||
super.writeText(text);
|
||||
updateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocused(boolean p_146195_1_) {
|
||||
if (!p_146195_1_ && isFocused()) {
|
||||
int result;
|
||||
try {
|
||||
result = Math.max(Integer.parseInt(getText()), 1);
|
||||
} catch (NumberFormatException e) {
|
||||
result = 1;
|
||||
}
|
||||
setText(String.valueOf(result));
|
||||
}
|
||||
super.setFocused(p_146195_1_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||
boolean flag = p_146192_1_ >= this.xPosition
|
||||
&& p_146192_1_ < this.xPosition + this.width
|
||||
&& p_146192_2_ >= this.yPosition
|
||||
&& p_146192_2_ < this.yPosition + this.height;
|
||||
if (flag && p_146192_3_ == 1) {
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String p_146180_1_) {
|
||||
super.setText(p_146180_1_);
|
||||
updateButtons();
|
||||
}
|
||||
};
|
||||
|
||||
list.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")));
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGuiLeft() {
|
||||
return guiLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGuiTop() {
|
||||
return guiTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getXSize() {
|
||||
return 176;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getYSize() {
|
||||
return 188;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addButton(GuiButton button) {
|
||||
getButtonList().add(button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeButton(GuiButton button) {
|
||||
getButtonList().remove(button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOverlayOffsetX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActionPerformed(GuiButton but) {
|
||||
actionPerformed(but);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<GuiButton> getButtonList() {
|
||||
return buttonList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMouseInput() {
|
||||
int delta = Mouse.getEventDWheel();
|
||||
if (delta != 0) list.handleDWheel(delta);
|
||||
super.handleMouseInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mX, int mY, int button) {
|
||||
key.mouseClicked(mX, mY, button);
|
||||
value.mouseClicked(mX, mY, button);
|
||||
super.mouseClicked(mX, mY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char aChar, int aKey) {
|
||||
if (key.textboxKeyTyped(aChar, aKey)) {
|
||||
updateButtons();
|
||||
return;
|
||||
}
|
||||
if (value.textboxKeyTyped(aChar, aKey)) return;
|
||||
super.keyTyped(aChar, aKey);
|
||||
}
|
||||
|
||||
private void updateButtons() {
|
||||
// this will be called from setText of key and value. NEVER UPDATE THE VALUE OF THESE HERE OR GET A
|
||||
// STACKOVERFLOW!
|
||||
boolean existing = ChannelDataAccessor.hasSubChannel(trigger, key.getText());
|
||||
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());
|
||||
}
|
||||
|
||||
private int getValue() {
|
||||
try {
|
||||
return Integer.parseInt(value.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton btn) {
|
||||
if (btn == null) return;
|
||||
switch (btn.id) {
|
||||
case 0:
|
||||
int value = getValue();
|
||||
if (value <= 0) return;
|
||||
ChannelDataAccessor.setChannelData(trigger, key.getText(), value);
|
||||
break;
|
||||
case 1:
|
||||
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||
break;
|
||||
case 2:
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
break;
|
||||
}
|
||||
super.actionPerformed(btn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
StructureLib.instance().proxy().uploadChannels(trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
super.updateScreen();
|
||||
key.updateCursorCounter();
|
||||
value.updateCursorCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesGuiPauseGame() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
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);
|
||||
key.drawTextBox();
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.value"), guiLeft + 12, guiTop + 142, 0);
|
||||
value.drawTextBox();
|
||||
}
|
||||
|
||||
private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> {
|
||||
private List<Entry<String, Integer>> cache;
|
||||
|
||||
public GuiChannelsList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
super(width, height, originX, originY, slotHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, Integer> getElementAt(int index) {
|
||||
return ChannelDataAccessor.iterateChannelData(trigger)
|
||||
.sorted()
|
||||
.skip(index)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumElements() {
|
||||
return ChannelDataAccessor.countChannelData(trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||
if (mXRelative >= margin + 1
|
||||
&& mXRelative <= margin + 5
|
||||
&& mYRelative >= margin / 2 + 1
|
||||
&& mYRelative <= margin / 2 + 5) {
|
||||
Entry<String, Integer> e = getElementAt(elementIndex);
|
||||
if (e != null) {
|
||||
ChannelDataAccessor.unsetChannelData(trigger, e.getKey());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.elementClicked(elementIndex, doubleClick, mXRelative, mYRelative);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareDrawElements() {
|
||||
cache = ChannelDataAccessor.iterateChannelData(trigger).sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawElement(
|
||||
int elementIndex, int x, int y, int height, Tessellator tessellator, boolean isHovering) {
|
||||
if (elementIndex < 0 || elementIndex >= cache.size()) {
|
||||
return;
|
||||
}
|
||||
Entry<String, Integer> e = cache.get(elementIndex);
|
||||
if (e == null) return;
|
||||
if (elementIndex > 0) {
|
||||
// args x1, x2, y1, color
|
||||
drawHorizontalLine(minX + 1, maxX - 1, y - 2, 0xffaaaaaa);
|
||||
}
|
||||
int keyMaxWidth = KEY_MAX_WIDTH * 2;
|
||||
if (isHovering) {
|
||||
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
drawTexturedModalRect(x + margin + 1, y + margin / 2 + 1, 251, 251, 5, 5);
|
||||
}
|
||||
fontRendererObj.drawString(trim(e.getKey(), keyMaxWidth - 9), x + margin + 8, y + margin / 2, 0xffffff);
|
||||
int valueX = x + margin + keyMaxWidth + margin;
|
||||
fontRendererObj.drawString(trim(e.getValue().toString(), maxX - valueX), valueX, y + margin / 2, 0xffffff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
super.drawScreen(mX, mY, partialTick);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Shamelessly stolen from EnderCore, with some amount of edit to fit in one file. Original code from CrazyPants.
|
||||
* <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
|
||||
*
|
||||
* Major edits
|
||||
* <ul>
|
||||
* <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to
|
||||
* not fire on other widgets</li>
|
||||
* <li>scrollbar are now rendered within width</li>
|
||||
* <li>added prepareDrawElements</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author CrazyPants
|
||||
* @author glee8e
|
||||
*/
|
||||
public abstract class GuiScrollableList<T> {
|
||||
protected final int slotHeight;
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
protected int originX;
|
||||
protected int originY;
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int minY;
|
||||
protected int maxY;
|
||||
protected int minX;
|
||||
protected int maxX;
|
||||
protected int mouseX;
|
||||
protected int mouseY;
|
||||
protected int selectedIndex = -1;
|
||||
protected int margin = 4;
|
||||
protected List<ListSelectionListener<T>> listeners = new CopyOnWriteArrayList<>();
|
||||
private int scrollUpButtonID;
|
||||
private int scrollDownButtonID;
|
||||
private float initialClickY = -2.0F;
|
||||
private float scrollMultiplier;
|
||||
private float amountScrolled;
|
||||
private long lastClickedTime;
|
||||
|
||||
private boolean showSelectionBox = true;
|
||||
|
||||
public GuiScrollableList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.originX = originX;
|
||||
this.originY = originY;
|
||||
this.slotHeight = slotHeight;
|
||||
|
||||
minY = originY;
|
||||
maxY = minY + height;
|
||||
minX = originX;
|
||||
maxX = minX + width;
|
||||
}
|
||||
|
||||
public void onGuiInit(IGuiScreen gui) {
|
||||
minY = originY + gui.getGuiTop();
|
||||
maxY = minY + height;
|
||||
minX = originX + gui.getGuiLeft();
|
||||
maxX = minX + width;
|
||||
}
|
||||
|
||||
public void addSelectionListener(ListSelectionListener<T> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeSelectionListener(ListSelectionListener<T> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public T getSelectedElement() {
|
||||
return getElementAt(selectedIndex);
|
||||
}
|
||||
|
||||
public void setSelection(T selection) {
|
||||
setSelection(getIndexOf(selection));
|
||||
}
|
||||
|
||||
public void setSelection(int index) {
|
||||
if (index == selectedIndex) {
|
||||
return;
|
||||
}
|
||||
selectedIndex = index;
|
||||
for (ListSelectionListener<T> listener : listeners) {
|
||||
listener.selectionChanged(this, selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOf(T element) {
|
||||
if (element == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < getNumElements(); i++) {
|
||||
if (element.equals(getElementAt(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public abstract T getElementAt(int index);
|
||||
|
||||
public abstract int getNumElements();
|
||||
|
||||
protected abstract void drawElement(
|
||||
int elementIndex, int x, int y, int height, Tessellator tessellator, boolean isHovering);
|
||||
|
||||
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setShowSelectionBox(boolean val) {
|
||||
showSelectionBox = val;
|
||||
}
|
||||
|
||||
protected int getContentHeight() {
|
||||
return getNumElements() * slotHeight;
|
||||
}
|
||||
|
||||
public void setScrollButtonIds(int scrollUpButtonID, int scrollDownButtonID) {
|
||||
this.scrollUpButtonID = scrollUpButtonID;
|
||||
this.scrollDownButtonID = scrollDownButtonID;
|
||||
}
|
||||
|
||||
private void clampScrollToBounds() {
|
||||
int i = getContentOverhang();
|
||||
if (i < 0) {
|
||||
i *= -1;
|
||||
}
|
||||
if (amountScrolled < 0.0F) {
|
||||
amountScrolled = 0.0F;
|
||||
}
|
||||
if (amountScrolled > i) {
|
||||
amountScrolled = i;
|
||||
}
|
||||
}
|
||||
|
||||
public int getContentOverhang() {
|
||||
return getContentHeight() - (height - margin);
|
||||
}
|
||||
|
||||
public void actionPerformed(GuiButton b) {
|
||||
if (b.enabled) {
|
||||
if (b.id == scrollUpButtonID) {
|
||||
amountScrolled -= slotHeight * 2 / 3;
|
||||
initialClickY = -2.0F;
|
||||
clampScrollToBounds();
|
||||
} else if (b.id == scrollDownButtonID) {
|
||||
amountScrolled += slotHeight * 2 / 3;
|
||||
initialClickY = -2.0F;
|
||||
clampScrollToBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.mouseX = mX;
|
||||
this.mouseY = mY;
|
||||
|
||||
processMouseEvents();
|
||||
|
||||
if (getContentOverhang() > 0) maxX = minX + width - 6;
|
||||
else maxX = minX + width;
|
||||
|
||||
clampScrollToBounds();
|
||||
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_FOG);
|
||||
|
||||
ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
int sx = minX * sr.getScaleFactor();
|
||||
int sw = width * sr.getScaleFactor();
|
||||
int sy = mc.displayHeight - (maxY * sr.getScaleFactor());
|
||||
int sh = height * sr.getScaleFactor();
|
||||
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GL11.glScissor(sx, sy, sw, sh);
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
drawContainerBackground(tessellator);
|
||||
|
||||
int amountScrolled = (int) this.amountScrolled;
|
||||
int contentYOffset = this.minY + margin - amountScrolled;
|
||||
|
||||
boolean hoveringPrecondition = mX >= minX && mX <= maxX && mY >= minY && mY <= maxY;
|
||||
|
||||
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) {
|
||||
|
||||
if (showSelectionBox && i == selectedIndex) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorOpaque_I(8421504);
|
||||
tessellator.addVertexWithUV(minX, elementY + slotHeight + 2, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX, elementY + slotHeight + 2, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX, elementY - 2, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(minX, elementY - 2, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.setColorOpaque_I(0);
|
||||
tessellator.addVertexWithUV(minX + 1, elementY + slotHeight + 1, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX - 1, elementY + slotHeight + 1, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX - 1, elementY - 1, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(minX + 1, elementY - 1, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
boolean isHovering = hoveringPrecondition && elementY - 2 <= mY && mY < elementY + slotHeight + 2;
|
||||
drawElement(i, minX, elementY, slotHeight, tessellator, isHovering);
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glShadeModel(GL11.GL_SMOOTH);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_I(0, 0);
|
||||
tessellator.addVertexWithUV(this.minX, this.minY + margin, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(this.maxX, this.minY + margin, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(this.maxX, this.minY, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(this.minX, this.minY, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(this.minX, this.maxY, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(this.maxX, this.maxY, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.setColorRGBA_I(0, 0);
|
||||
tessellator.addVertexWithUV(this.maxX, this.maxY - margin, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(this.minX, this.maxY - margin, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
|
||||
renderScrollBar(tessellator);
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glShadeModel(GL11.GL_FLAT);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
protected void prepareDrawElements() {}
|
||||
|
||||
protected void renderScrollBar(Tessellator tessellator) {
|
||||
|
||||
int contentHeightOverBounds = getContentOverhang();
|
||||
if (contentHeightOverBounds > 0) {
|
||||
|
||||
int clear = (maxY - minY) * (maxY - minY) / getContentHeight();
|
||||
|
||||
if (clear < 32) {
|
||||
clear = 32;
|
||||
}
|
||||
|
||||
if (clear > maxY - minY - 8) {
|
||||
clear = maxY - minY - 8;
|
||||
}
|
||||
|
||||
int y = (int) this.amountScrolled * (maxY - minY - clear) / contentHeightOverBounds + minY;
|
||||
if (y < minY) {
|
||||
y = minY;
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
int scrollBarMinX = getScrollBarX();
|
||||
int scrollBarMaxX = scrollBarMinX + 6;
|
||||
tessellator.startDrawingQuads();
|
||||
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, maxY, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, maxY, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, minY, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, minY, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.setColorRGBA_F(0.3f, 0.3f, 0.3f, 1);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, (y + clear), 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, (y + clear), 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, y, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.setColorRGBA_F(0.7f, 0.7f, 0.7f, 1);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, (y + clear - 1), 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV((scrollBarMaxX - 1), (y + clear - 1), 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV((scrollBarMaxX - 1), y, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
private void processMouseEvents() {
|
||||
if (Mouse.isButtonDown(0)) {
|
||||
processMouseBown();
|
||||
} else {
|
||||
initialClickY = -1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDWheel(int mouseWheelDelta) {
|
||||
if (!Mouse.isButtonDown(0)) {
|
||||
if (mouseWheelDelta > 0) {
|
||||
amountScrolled -= slotHeight / 2;
|
||||
} else /* if (mouseWheelDelta < 0) */ {
|
||||
amountScrolled += slotHeight / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processMouseBown() {
|
||||
int contentHeightOverBounds;
|
||||
if (initialClickY == -1.0F) {
|
||||
|
||||
if (mouseY >= minY && mouseY <= maxY && mouseX >= minX && mouseX <= maxX + 6) {
|
||||
|
||||
boolean clickInBounds = true;
|
||||
|
||||
int y = mouseY - minY + (int) amountScrolled - margin;
|
||||
int mouseOverElement = y / slotHeight;
|
||||
|
||||
if (mouseX >= minX
|
||||
&& mouseX <= maxX
|
||||
&& mouseOverElement >= 0
|
||||
&& y >= 0
|
||||
&& mouseOverElement < getNumElements()) {
|
||||
boolean doubleClick =
|
||||
mouseOverElement == selectedIndex && Minecraft.getSystemTime() - lastClickedTime < 250L;
|
||||
if (elementClicked(
|
||||
mouseOverElement, doubleClick, mouseX - minX, y - slotHeight * mouseOverElement)) {
|
||||
setSelection(mouseOverElement);
|
||||
lastClickedTime = Minecraft.getSystemTime();
|
||||
}
|
||||
} else if (mouseX >= minX && mouseX <= maxX && y < 0) {
|
||||
clickInBounds = false;
|
||||
}
|
||||
|
||||
int scrollBarMinX = getScrollBarX();
|
||||
int scrollBarMaxX = scrollBarMinX + 6;
|
||||
if (mouseX >= scrollBarMinX && mouseX <= scrollBarMaxX) {
|
||||
|
||||
scrollMultiplier = -1.0F;
|
||||
contentHeightOverBounds = getContentOverhang();
|
||||
|
||||
if (contentHeightOverBounds < 1) {
|
||||
contentHeightOverBounds = 1;
|
||||
}
|
||||
|
||||
int empty = (int) ((float) ((maxY - minY) * (maxY - minY)) / (float) getContentHeight());
|
||||
if (empty < 32) {
|
||||
empty = 32;
|
||||
}
|
||||
if (empty > maxY - minY - 8) {
|
||||
empty = maxY - minY - 8;
|
||||
}
|
||||
scrollMultiplier /= (float) (maxY - minY - empty) / (float) contentHeightOverBounds;
|
||||
|
||||
} else {
|
||||
scrollMultiplier = 1.0F;
|
||||
}
|
||||
|
||||
if (clickInBounds) {
|
||||
initialClickY = mouseY;
|
||||
} else {
|
||||
initialClickY = -2.0F;
|
||||
}
|
||||
|
||||
} else {
|
||||
initialClickY = -2.0F;
|
||||
}
|
||||
|
||||
} else if (initialClickY >= 0.0F) {
|
||||
// Scrolling
|
||||
amountScrolled -= (mouseY - initialClickY) * scrollMultiplier;
|
||||
initialClickY = mouseY;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getScrollBarX() {
|
||||
return maxX;
|
||||
}
|
||||
|
||||
protected void drawContainerBackground(Tessellator tess) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tess.startDrawingQuads();
|
||||
tess.setColorOpaque_I(2105376);
|
||||
tess.addVertex(minX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, minY, 0.0D);
|
||||
tess.addVertex(minX, minY, 0.0D);
|
||||
tess.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
public interface IGuiScreen {
|
||||
|
||||
int getGuiLeft();
|
||||
|
||||
int getGuiTop();
|
||||
|
||||
int getXSize();
|
||||
|
||||
int getYSize();
|
||||
|
||||
void addButton(GuiButton button);
|
||||
|
||||
void removeButton(GuiButton button);
|
||||
|
||||
int getOverlayOffsetX();
|
||||
|
||||
void doActionPerformed(GuiButton but);
|
||||
}
|
||||
|
||||
public interface ListSelectionListener<T> {
|
||||
|
||||
void selectionChanged(GuiScrollableList<T> list, int selectedIndex);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package com.gtnewhorizon.structurelib.item;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
import static net.minecraft.util.EnumChatFormatting.BLUE;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
import static net.minecraft.util.StatCollector.translateToLocalFormatted;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
||||
import java.util.List;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -13,6 +15,7 @@ import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
public class ItemConstructableTrigger extends Item {
|
||||
public ItemConstructableTrigger() {
|
||||
@@ -21,6 +24,13 @@ public class ItemConstructableTrigger extends Item {
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null)
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemUseFirst(
|
||||
ItemStack stack,
|
||||
@@ -39,19 +49,31 @@ public class ItemConstructableTrigger extends Item {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
|
||||
aList.add(
|
||||
translateToLocal("item.structurelib.constructableTrigger.desc.0")); // Triggers Constructable Interface
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.1")); // Shows multiblock construction details,
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.2")); // just Use on a multiblock controller.
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.3")); // (Sneak Use in creative to build)
|
||||
aList.add(BLUE
|
||||
+ translateToLocal("item.structurelib.constructableTrigger.desc.4")); // Quantity affects tier/mode/type
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
|
||||
aList.add(translateToLocalFormatted(
|
||||
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||
ChannelDataAccessor.countChannelData(aStack)));
|
||||
ChannelDataAccessor.iterateChannelData(aStack)
|
||||
.map(e -> e.getKey() + ": " + e.getValue())
|
||||
.forEach(aList::add);
|
||||
} else {
|
||||
aList.add(translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.0")); // Triggers Constructable Interface
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.1")); // Shows multiblock construction details,
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.2")); // just Use on a multiblock controller.
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.3")); // (Sneak Use in creative to build)
|
||||
aList.add(BLUE
|
||||
+ translateToLocal(
|
||||
"item.structurelib.constructableTrigger.desc.4")); // Quantity affects tier/mode/type
|
||||
if (ChannelDataAccessor.hasSubChannel(aStack))
|
||||
aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.5"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import cpw.mods.fml.common.network.ByteBufUtils;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public class SetChannelDataMessage implements IMessage {
|
||||
private List<Map.Entry<String, Integer>> data = new ArrayList<>();
|
||||
|
||||
public SetChannelDataMessage() {}
|
||||
|
||||
public SetChannelDataMessage(ItemStack trigger) {
|
||||
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
data.clear();
|
||||
int size = ByteBufUtils.readVarShort(buf);
|
||||
for (int i = 0; i < size; i++) {
|
||||
data.add(Pair.of(ByteBufUtils.readUTF8String(buf), ByteBufUtils.readVarInt(buf, 4)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
ByteBufUtils.writeVarShort(buf, data.size());
|
||||
for (Entry<String, Integer> e : data) {
|
||||
ByteBufUtils.writeUTF8String(buf, e.getKey());
|
||||
ByteBufUtils.writeVarInt(buf, e.getValue(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<SetChannelDataMessage, IMessage> {
|
||||
@Override
|
||||
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
|
||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||
ItemStack heldItem = player.getHeldItem();
|
||||
if (heldItem != null && heldItem.getItem() instanceof ItemConstructableTrigger) {
|
||||
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||
for (Entry<String, Integer> e : message.data) {
|
||||
ChannelDataAccessor.setChannelData(heldItem, e.getKey(), e.getValue());
|
||||
}
|
||||
// since this is a set all channel request from the client, we would assume client already know
|
||||
// what this would look like on the client, so no sync
|
||||
} else {
|
||||
StructureLib.LOGGER.warn(
|
||||
"{} trying to set channel data on {}, which is not a hologram projector!",
|
||||
player.getUniqueID(),
|
||||
heldItem);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import static java.lang.Integer.MIN_VALUE;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
||||
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
|
||||
@@ -1895,6 +1896,42 @@ public class StructureUtility {
|
||||
|
||||
// endregion
|
||||
|
||||
// region channels
|
||||
|
||||
/**
|
||||
* See channels.md in docs folder
|
||||
*/
|
||||
public static <T> IStructureElement<T> withChannel(String channel, IStructureElement<T> backing) {
|
||||
return new IStructureElement<T>() {
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
return backing.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return backing.spawnHint(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel));
|
||||
}
|
||||
|
||||
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return backing.placeBlock(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel));
|
||||
}
|
||||
|
||||
public PlaceResult survivalPlaceBlock(
|
||||
T t,
|
||||
World world,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
ItemStack trigger,
|
||||
IItemSource s,
|
||||
EntityPlayerMP actor,
|
||||
Consumer<IChatComponent> chatter) {
|
||||
return backing.survivalPlaceBlock(
|
||||
t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel), s, actor, chatter);
|
||||
}
|
||||
};
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* Used internally, to generate skips for structure definitions
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,15 @@ item.structurelib.constructableTrigger.desc.1=Shows multiblock construction sche
|
||||
item.structurelib.constructableTrigger.desc.2=use on the multiblock controller.
|
||||
item.structurelib.constructableTrigger.desc.3=(Sneak use on controller to build)
|
||||
item.structurelib.constructableTrigger.desc.4=Quantity of schematics in item stack affects tier/mode/type
|
||||
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 ticks before another autoplace...
|
||||
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
|
||||
|
||||
structurelib.autoplace.built_stat=Built %s blocks
|
||||
structurelib.autoplace.complete=§aThis multiblock has completed!
|
||||
|
||||
@@ -26,8 +26,17 @@ item.structurelib.constructableTrigger.desc.1=只能在多方块主机上使用
|
||||
item.structurelib.constructableTrigger.desc.2=结构的示意图。 (译者注:请搭高使用)
|
||||
item.structurelib.constructableTrigger.desc.3= (潜行右键可以直接完成搭建)
|
||||
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=请在下一次自动搭建前等待%dtick...
|
||||
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全部清空
|
||||
|
||||
structurelib.autoplace.built_stat=已放置 %s 个方块
|
||||
structurelib.autoplace.complete=§a这个多方块结构已经完成自动搭建!
|
||||
structurelib.autoplace.error.not_enabled=§c这个多方块结构不支持在生存模式下自动搭建!
|
||||
structurelib.autoplace.error.no_simple_block=§c建议放置§r%s§c,但没有找到它
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
Reference in new issue
Block a user