mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-09 04:55:01 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebae6436e6 | ||
|
|
3805ae664c | ||
|
|
d019b9558b | ||
|
|
22cef2e28f | ||
|
|
d05ac45924 | ||
|
|
bd76be4f6f | ||
|
|
d3e258dd30 | ||
|
|
0999964f1a |
No files matched your search
Executable → Regular
File mode changed.
+1
-2
@@ -1,7 +1,7 @@
|
||||
javadoc {
|
||||
javadocTool = javaToolchains.javadocToolFor {
|
||||
// Last version with frames, but it also has search
|
||||
languageVersion = JavaLanguageVersion.of(11)
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
vendor = JvmVendorSpec.ADOPTIUM
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ javadoc {
|
||||
overview "src/main/javadoc/overview.html"
|
||||
links 'https://docs.oracle.com/javase/8/docs/api/'
|
||||
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
|
||||
addBooleanOption("-frames", true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -1,9 +1,13 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.6.13:dev")
|
||||
runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.7.29-GTNH:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.6.19:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.38-GTNH:dev")
|
||||
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
|
||||
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.227:dev") {
|
||||
// exclude group: "com.github.GTNewHorizons", module: "StructureLib"
|
||||
// }
|
||||
}
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.37'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.38'
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -42,17 +43,23 @@ import org.lwjgl.opengl.GL11;
|
||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
@@ -259,6 +266,13 @@ public class ClientProxy extends CommonProxy {
|
||||
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FMLInitializationEvent e) {
|
||||
super.init(e);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(new KeybindHandler());
|
||||
}
|
||||
|
||||
static void markTextureUsed(IIcon icon) {
|
||||
if (StructureLib.COMPAT instanceof IStructureCompat)
|
||||
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
|
||||
@@ -654,4 +668,30 @@ public class ClientProxy extends CommonProxy {
|
||||
p.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeybindHandler {
|
||||
|
||||
private static final KeyBinding CYCLE_PROJECTOR_MODE = new KeyBinding(
|
||||
"item.structurelib.constructableTrigger.keys.cycleMode",
|
||||
0,
|
||||
"item.structurelib.constructableTrigger.keyCategory");
|
||||
|
||||
public KeybindHandler() {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
// If somebody wants to make a UI element for this, be my guest
|
||||
ClientRegistry.registerKeyBinding(CYCLE_PROJECTOR_MODE);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@SubscribeEvent
|
||||
public void key(InputEvent.KeyInputEvent event) {
|
||||
if (CYCLE_PROJECTOR_MODE.isPressed()) {
|
||||
cycleProjectorMode();
|
||||
}
|
||||
}
|
||||
|
||||
private static void cycleProjectorMode() {
|
||||
StructureLib.net.sendToServer(new StructureProjectorCycleModeMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
@@ -64,6 +65,8 @@ public class CommonProxy {
|
||||
|
||||
public void preInit(FMLPreInitializationEvent e) {}
|
||||
|
||||
public void init(FMLInitializationEvent e) {}
|
||||
|
||||
public long getOverworldTime() {
|
||||
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -20,13 +27,15 @@ import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
@@ -40,7 +49,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 +66,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(
|
||||
@@ -70,6 +79,11 @@ public class StructureLib {
|
||||
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
|
||||
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
|
||||
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
|
||||
net.registerMessage(
|
||||
StructureProjectorCycleModeMessage.Handler.class,
|
||||
StructureProjectorCycleModeMessage.class,
|
||||
6,
|
||||
Side.SERVER);
|
||||
|
||||
try {
|
||||
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
|
||||
@@ -84,7 +98,7 @@ public class StructureLib {
|
||||
|
||||
@Mod.Instance
|
||||
static StructureLib INSTANCE;
|
||||
|
||||
@Mod.Instance(STRUCTURECOMPAT_MODID)
|
||||
static Object COMPAT;
|
||||
|
||||
static Block blockHint;
|
||||
@@ -100,8 +114,6 @@ public class StructureLib {
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean isGTLoaded;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent e) {
|
||||
ConfigurationHandler.INSTANCE.init(e.getSuggestedConfigurationFile());
|
||||
@@ -117,11 +129,15 @@ public class StructureLib {
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||
|
||||
InventoryUtility.init();
|
||||
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
|
||||
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
|
||||
}
|
||||
|
||||
isGTLoaded = Loader.isModLoaded("gregtech");
|
||||
ChannelDescription.set(CHANNEL_SHOW_ERROR, MOD_ID, "channels.structurelib.show_errors");
|
||||
|
||||
DevelopHelper.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent e) {
|
||||
proxy.init(e);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
@@ -135,6 +151,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);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
@@ -43,6 +45,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<>();
|
||||
|
||||
/**
|
||||
@@ -350,4 +353,35 @@ 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,
|
||||
() -> StatCollector
|
||||
.translateToLocalFormatted("structurelib.tooltip.channelvalue", channelValue, channel));
|
||||
}
|
||||
}
|
||||
+8
-9
@@ -57,12 +57,12 @@ public class ConstructableUtility {
|
||||
}
|
||||
|
||||
IConstructable constructable = null;
|
||||
if (tTileEntity instanceof IConstructableProvider)
|
||||
constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
||||
else if (tTileEntity instanceof IConstructable) {
|
||||
constructable = (IConstructable) tTileEntity;
|
||||
if (tTileEntity instanceof IConstructableProvider aConstructableProvider)
|
||||
constructable = aConstructableProvider.getConstructable();
|
||||
else if (tTileEntity instanceof IConstructable aConstructable) {
|
||||
constructable = aConstructable;
|
||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
||||
ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
|
||||
ExtendedFacing facing = tTileEntity instanceof IAlignment alignment ? alignment.getExtendedFacing()
|
||||
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
|
||||
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
|
||||
.toConstructable(tTileEntity, facing);
|
||||
@@ -70,14 +70,13 @@ public class ConstructableUtility {
|
||||
|
||||
if (constructable == null) return false;
|
||||
|
||||
if (aPlayer instanceof EntityPlayerMP) {
|
||||
EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer;
|
||||
if (aPlayer instanceof EntityPlayerMP playerMP) {
|
||||
// server side and sneaking (already checked above)
|
||||
// do construct
|
||||
if (aPlayer.capabilities.isCreativeMode) {
|
||||
constructable.construct(aStack, false);
|
||||
} else if (constructable instanceof ISurvivalConstructable) {
|
||||
int built = ((ISurvivalConstructable) constructable).survivalConstruct(
|
||||
} else if (constructable instanceof ISurvivalConstructable aConstructable) {
|
||||
int built = aConstructable.survivalConstruct(
|
||||
aStack,
|
||||
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
|
||||
ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
|
||||
|
||||
@@ -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,48 +1,82 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import static org.lwjgl.opengl.ARBImaging.*;
|
||||
import static org.lwjgl.opengl.ARBImaging.GL_SEPARABLE_2D;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL11.GL_ALPHA_TEST_FUNC;
|
||||
import static org.lwjgl.opengl.GL12.GL_RESCALE_NORMAL;
|
||||
import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D;
|
||||
import static org.lwjgl.opengl.GL13.*;
|
||||
import static org.lwjgl.opengl.GL14.*;
|
||||
import static org.lwjgl.opengl.GL14.GL_BLEND_SRC_RGB;
|
||||
import static org.lwjgl.opengl.GL15.GL_FOG_COORD_ARRAY;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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 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.collect.ImmutableMap;
|
||||
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 SHOW_ERROR_BTN = 3;
|
||||
private static final int GT_NO_HATCH_BTN = 4;
|
||||
|
||||
private static final String SHOW_ERROR_CHANNEL = "show_error";
|
||||
private static final String GT_NO_HATCH_CHANNEL = "gt_no_hatch";
|
||||
|
||||
private static final int KEY_MAX_WIDTH = 50;
|
||||
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;
|
||||
|
||||
public GuiScreenConfigureChannels(ItemStack trigger) {
|
||||
public GuiScreenConfigureChannels(Container container, ItemStack trigger) {
|
||||
super(container);
|
||||
|
||||
this.trigger = trigger;
|
||||
list = new GuiChannelsList(152, 100, 12, 12, 14);
|
||||
list.addSelectionListener((list, selectedIndex) -> {
|
||||
@@ -51,6 +85,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
value.setText(e.getValue().toString());
|
||||
updateButtons();
|
||||
});
|
||||
autoCompleteList = new GuiAutoCompleteList(151 + 12 - 45 - 2, 50, 45, 119 + 12, 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,6 +171,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
};
|
||||
|
||||
list.onGuiInit(this);
|
||||
autoCompleteList.onGuiInit(this);
|
||||
|
||||
addButton(
|
||||
new GuiButton(
|
||||
@@ -162,21 +198,8 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
|
||||
|
||||
addButton(
|
||||
new GuiButton(
|
||||
SHOW_ERROR_BTN,
|
||||
StructureLib.isGTLoaded ? guiLeft + 12 : guiLeft + 52,
|
||||
guiTop + 180,
|
||||
73,
|
||||
20,
|
||||
""));
|
||||
|
||||
// only show GT hatch button if GT is loaded
|
||||
if (StructureLib.isGTLoaded) {
|
||||
addButton(new GuiButton(GT_NO_HATCH_BTN, guiLeft + 92, guiTop + 180, 73, 20, ""));
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
tooltipSplitCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,7 +219,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
|
||||
@Override
|
||||
public int getYSize() {
|
||||
return 211;
|
||||
return 188;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,13 +275,14 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
|
||||
@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()) {
|
||||
@@ -271,15 +295,31 @@ 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);
|
||||
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();
|
||||
@@ -300,20 +340,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
&& Integer.parseInt(value.getText()) > 0;
|
||||
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, SHOW_ERROR_CHANNEL)) {
|
||||
getButtonList().get(SHOW_ERROR_BTN).displayString = I18n.format(I18N_PREFIX + "error.hide");
|
||||
} else {
|
||||
getButtonList().get(SHOW_ERROR_BTN).displayString = I18n.format(I18N_PREFIX + "error.show");
|
||||
}
|
||||
|
||||
// this button only exists if GT is loaded.
|
||||
if (StructureLib.isGTLoaded) {
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, GT_NO_HATCH_CHANNEL)) {
|
||||
getButtonList().get(GT_NO_HATCH_BTN).displayString = I18n.format(I18N_PREFIX + "gt_no_hatch.disable");
|
||||
} else {
|
||||
getButtonList().get(GT_NO_HATCH_BTN).displayString = I18n.format(I18N_PREFIX + "gt_no_hatch.enable");
|
||||
}
|
||||
}
|
||||
autoCompleteList.reset();
|
||||
}
|
||||
|
||||
private int getValue() {
|
||||
@@ -338,20 +365,8 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
break;
|
||||
case WIPE_BTN:
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
break;
|
||||
case SHOW_ERROR_BTN:
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, SHOW_ERROR_CHANNEL)) {
|
||||
ChannelDataAccessor.unsetChannelData(trigger, SHOW_ERROR_CHANNEL);
|
||||
} else {
|
||||
ChannelDataAccessor.setChannelData(trigger, SHOW_ERROR_CHANNEL, 1);
|
||||
}
|
||||
break;
|
||||
case GT_NO_HATCH_BTN:
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, GT_NO_HATCH_CHANNEL)) {
|
||||
ChannelDataAccessor.unsetChannelData(trigger, GT_NO_HATCH_CHANNEL);
|
||||
} else {
|
||||
ChannelDataAccessor.setChannelData(trigger, GT_NO_HATCH_CHANNEL, 1);
|
||||
}
|
||||
key.setText("");
|
||||
this.value.setText("");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -375,17 +390,11 @@ 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"),
|
||||
@@ -401,6 +410,315 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawChannelDescriptionTooltip(String candidate) {
|
||||
List<String> tooltip = tooltipSplitCache.computeIfAbsent(candidate, this::getChannelDescriptionTooltip);
|
||||
drawHoveringText(tooltip, guiLeft + 151 + 12, guiTop + 122, fontRendererObj);
|
||||
}
|
||||
|
||||
public static void debugPrintGLEnable() {
|
||||
Map<Integer, String> functions = ImmutableMap.<Integer, String>builder().put(GL11.GL_NEVER, "GL_NEVER")
|
||||
.put(GL11.GL_LESS, "GL_LESS").put(GL11.GL_EQUAL, "GL_EQUAL").put(GL11.GL_LEQUAL, "GL_LEQUAL")
|
||||
.put(GL11.GL_GREATER, "GL_GREATER").put(GL11.GL_NOTEQUAL, "GL_NOTEQUAL")
|
||||
.put(GL11.GL_GEQUAL, "GL_GEQUAL").put(GL11.GL_ALWAYS, "GL_ALWAYS").put(GL11.GL_ZERO, "GL_ZERO")
|
||||
.put(GL11.GL_ONE, "GL_ONE").put(GL11.GL_SRC_COLOR, "GL_SRC_COLOR")
|
||||
.put(GL11.GL_ONE_MINUS_SRC_COLOR, "GL_ONE_MINUS_SRC_COLOR").put(GL11.GL_DST_COLOR, "GL_DST_COLOR")
|
||||
.put(GL11.GL_ONE_MINUS_DST_COLOR, "GL_ONE_MINUS_DST_COLOR").put(GL11.GL_SRC_ALPHA, "GL_SRC_ALPHA")
|
||||
.put(GL11.GL_ONE_MINUS_SRC_ALPHA, "GL_ONE_MINUS_SRC_ALPHA").put(GL11.GL_DST_ALPHA, "GL_DST_ALPHA")
|
||||
.put(GL11.GL_ONE_MINUS_DST_ALPHA, "GL_ONE_MINUS_DST_ALPHA")
|
||||
.put(GL11.GL_CONSTANT_COLOR, "GL_CONSTANT_COLOR")
|
||||
.put(GL11.GL_ONE_MINUS_CONSTANT_COLOR, "GL_ONE_MINUS_CONSTANT_COLOR")
|
||||
.put(GL11.GL_CONSTANT_ALPHA, "GL_CONSTANT_ALPHA")
|
||||
.put(GL11.GL_ONE_MINUS_CONSTANT_ALPHA, "GL_ONE_MINUS_CONSTANT_ALPHA")
|
||||
.put(GL11.GL_SRC_ALPHA_SATURATE, "GL_SRC_ALPHA_SATURATE").build();
|
||||
System.out.println("GL_ALPHA_TEST: " + GL11.glIsEnabled(GL_ALPHA_TEST));
|
||||
System.out.println("GL_AUTO_NORMAL: " + GL11.glIsEnabled(GL_AUTO_NORMAL));
|
||||
System.out.println("GL_BLEND: " + GL11.glIsEnabled(GL_BLEND));
|
||||
System.out.println("GL_COLOR_ARRAY: " + GL11.glIsEnabled(GL_COLOR_ARRAY));
|
||||
System.out.println("GL_COLOR_LOGIC_OP: " + GL11.glIsEnabled(GL_COLOR_LOGIC_OP));
|
||||
System.out.println("GL_COLOR_MATERIAL: " + GL11.glIsEnabled(GL_COLOR_MATERIAL));
|
||||
System.out.println("GL_COLOR_SUM: " + GL11.glIsEnabled(GL_COLOR_SUM));
|
||||
System.out.println("GL_COLOR_TABLE: " + GL11.glIsEnabled(GL_COLOR_TABLE));
|
||||
System.out.println("GL_CONVOLUTION_1D: " + GL11.glIsEnabled(GL_CONVOLUTION_1D));
|
||||
System.out.println("GL_CONVOLUTION_2D: " + GL11.glIsEnabled(GL_CONVOLUTION_2D));
|
||||
System.out.println("GL_CULL_FACE: " + GL11.glIsEnabled(GL_CULL_FACE));
|
||||
System.out.println("GL_DEPTH_TEST: " + GL11.glIsEnabled(GL_DEPTH_TEST));
|
||||
System.out.println("GL_DITHER: " + GL11.glIsEnabled(GL_DITHER));
|
||||
System.out.println("GL_EDGE_FLAG_ARRAY: " + GL11.glIsEnabled(GL_EDGE_FLAG_ARRAY));
|
||||
System.out.println("GL_FOG: " + GL11.glIsEnabled(GL_FOG));
|
||||
System.out.println("GL_FOG_COORD_ARRAY: " + GL11.glIsEnabled(GL_FOG_COORD_ARRAY));
|
||||
System.out.println("GL_HISTOGRAM: " + GL11.glIsEnabled(GL_HISTOGRAM));
|
||||
System.out.println("GL_INDEX_ARRAY: " + GL11.glIsEnabled(GL_INDEX_ARRAY));
|
||||
System.out.println("GL_INDEX_LOGIC_OP: " + GL11.glIsEnabled(GL_INDEX_LOGIC_OP));
|
||||
System.out.println("GL_LIGHTING: " + GL11.glIsEnabled(GL_LIGHTING));
|
||||
System.out.println("GL_LINE_SMOOTH: " + GL11.glIsEnabled(GL_LINE_SMOOTH));
|
||||
System.out.println("GL_LINE_STIPPLE: " + GL11.glIsEnabled(GL_LINE_STIPPLE));
|
||||
System.out.println("GL_MAP1_COLOR_4: " + GL11.glIsEnabled(GL_MAP1_COLOR_4));
|
||||
System.out.println("GL_MAP1_INDEX: " + GL11.glIsEnabled(GL_MAP1_INDEX));
|
||||
System.out.println("GL_MAP1_NORMAL: " + GL11.glIsEnabled(GL_MAP1_NORMAL));
|
||||
System.out.println("GL_MAP1_TEXTURE_COORD_1: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_1));
|
||||
System.out.println("GL_MAP1_TEXTURE_COORD_2: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_2));
|
||||
System.out.println("GL_MAP1_TEXTURE_COORD_3: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_3));
|
||||
System.out.println("GL_MAP1_TEXTURE_COORD_4: " + GL11.glIsEnabled(GL_MAP1_TEXTURE_COORD_4));
|
||||
System.out.println("GL_MAP2_COLOR_4: " + GL11.glIsEnabled(GL_MAP2_COLOR_4));
|
||||
System.out.println("GL_MAP2_INDEX: " + GL11.glIsEnabled(GL_MAP2_INDEX));
|
||||
System.out.println("GL_MAP2_NORMAL: " + GL11.glIsEnabled(GL_MAP2_NORMAL));
|
||||
System.out.println("GL_MAP2_TEXTURE_COORD_1: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_1));
|
||||
System.out.println("GL_MAP2_TEXTURE_COORD_2: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_2));
|
||||
System.out.println("GL_MAP2_TEXTURE_COORD_3: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_3));
|
||||
System.out.println("GL_MAP2_TEXTURE_COORD_4: " + GL11.glIsEnabled(GL_MAP2_TEXTURE_COORD_4));
|
||||
System.out.println("GL_MAP2_VERTEX_3: " + GL11.glIsEnabled(GL_MAP2_VERTEX_3));
|
||||
System.out.println("GL_MAP2_VERTEX_4: " + GL11.glIsEnabled(GL_MAP2_VERTEX_4));
|
||||
System.out.println("GL_MINMAX: " + GL11.glIsEnabled(GL_MINMAX));
|
||||
System.out.println("GL_MULTISAMPLE: " + GL11.glIsEnabled(GL_MULTISAMPLE));
|
||||
System.out.println("GL_NORMAL_ARRAY: " + GL11.glIsEnabled(GL_NORMAL_ARRAY));
|
||||
System.out.println("GL_NORMALIZE: " + GL11.glIsEnabled(GL_NORMALIZE));
|
||||
System.out.println("GL_POINT_SMOOTH: " + GL11.glIsEnabled(GL_POINT_SMOOTH));
|
||||
System.out.println("GL_POINT_SPRITE: " + GL11.glIsEnabled(GL_POINT_SPRITE));
|
||||
System.out.println("GL_POLYGON_SMOOTH: " + GL11.glIsEnabled(GL_POLYGON_SMOOTH));
|
||||
System.out.println("GL_POLYGON_OFFSET_FILL: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_FILL));
|
||||
System.out.println("GL_POLYGON_OFFSET_LINE: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_LINE));
|
||||
System.out.println("GL_POLYGON_OFFSET_POINT: " + GL11.glIsEnabled(GL_POLYGON_OFFSET_POINT));
|
||||
System.out.println("GL_POLYGON_STIPPLE: " + GL11.glIsEnabled(GL_POLYGON_STIPPLE));
|
||||
System.out.println("GL_POST_COLOR_MATRIX_COLOR_TABLE: " + GL11.glIsEnabled(GL_POST_COLOR_MATRIX_COLOR_TABLE));
|
||||
System.out.println("GL_POST_CONVOLUTION_COLOR_TABLE: " + GL11.glIsEnabled(GL_POST_CONVOLUTION_COLOR_TABLE));
|
||||
System.out.println("GL_RESCALE_NORMAL: " + GL11.glIsEnabled(GL_RESCALE_NORMAL));
|
||||
System.out.println("GL_SAMPLE_ALPHA_TO_COVERAGE: " + GL11.glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
|
||||
System.out.println("GL_SAMPLE_ALPHA_TO_ONE: " + GL11.glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE));
|
||||
System.out.println("GL_SAMPLE_COVERAGE: " + GL11.glIsEnabled(GL_SAMPLE_COVERAGE));
|
||||
System.out.println("GL_SCISSOR_TEST: " + GL11.glIsEnabled(GL_SCISSOR_TEST));
|
||||
System.out.println("GL_SECONDARY_COLOR_ARRAY: " + GL11.glIsEnabled(GL_SECONDARY_COLOR_ARRAY));
|
||||
System.out.println("GL_SEPARABLE_2D: " + GL11.glIsEnabled(GL_SEPARABLE_2D));
|
||||
System.out.println("GL_STENCIL_TEST: " + GL11.glIsEnabled(GL_STENCIL_TEST));
|
||||
System.out.println("GL_TEXTURE_1D: " + GL11.glIsEnabled(GL_TEXTURE_1D));
|
||||
System.out.println("GL_TEXTURE_2D: " + GL11.glIsEnabled(GL_TEXTURE_2D));
|
||||
System.out.println("GL_TEXTURE_3D: " + GL11.glIsEnabled(GL_TEXTURE_3D));
|
||||
System.out.println("GL_TEXTURE_COORD_ARRAY: " + GL11.glIsEnabled(GL_TEXTURE_COORD_ARRAY));
|
||||
System.out.println("GL_TEXTURE_CUBE_MAP: " + GL11.glIsEnabled(GL_TEXTURE_CUBE_MAP));
|
||||
System.out.println("GL_TEXTURE_GEN_Q: " + GL11.glIsEnabled(GL_TEXTURE_GEN_Q));
|
||||
System.out.println("GL_TEXTURE_GEN_R: " + GL11.glIsEnabled(GL_TEXTURE_GEN_R));
|
||||
System.out.println("GL_TEXTURE_GEN_S: " + GL11.glIsEnabled(GL_TEXTURE_GEN_S));
|
||||
System.out.println("GL_TEXTURE_GEN_T: " + GL11.glIsEnabled(GL_TEXTURE_GEN_T));
|
||||
System.out.println("GL_VERTEX_ARRAY: " + GL11.glIsEnabled(GL_VERTEX_ARRAY));
|
||||
System.out.println("GL_VERTEX_PROGRAM_POINT_SIZE: " + GL11.glIsEnabled(GL_VERTEX_PROGRAM_POINT_SIZE));
|
||||
System.out.println("GL_VERTEX_PROGRAM_TWO_SIDE: " + GL11.glIsEnabled(GL_VERTEX_PROGRAM_TWO_SIDE));
|
||||
System.out.println("Depth Function: " + functions.get(GL11.glGetInteger(GL_DEPTH_FUNC)));
|
||||
System.out.printf(
|
||||
"Blend Function: DST: Alpha %s RGB %s, SRC: Alpha %s RGB %s\n",
|
||||
functions.get(GL11.glGetInteger(GL_BLEND_DST_ALPHA)),
|
||||
functions.get(GL11.glGetInteger(GL_BLEND_DST_RGB)),
|
||||
functions.get(GL11.glGetInteger(GL_BLEND_SRC_ALPHA)),
|
||||
functions.get(GL11.glGetInteger(GL_BLEND_SRC_RGB)));
|
||||
System.out.println(
|
||||
"Alpha Test Function: " + functions.get(GL11.glGetInteger(GL_ALPHA_TEST_FUNC))
|
||||
+ ", Reference: "
|
||||
+ GL11.glGetDouble(GL11.GL_ALPHA_TEST_REF));
|
||||
}
|
||||
|
||||
private List<String> getChannelDescriptionTooltip(String candidate) {
|
||||
List<String> tooltip = new ArrayList<>();
|
||||
int maxLine = width - (guiLeft + 151 + 12) - 12;
|
||||
for (Entry<String, String> e : ChannelDescription.get(candidate).getDescriptions().entrySet()) {
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(
|
||||
StatCollector.translateToLocalFormatted(
|
||||
"item.structurelib.constructableTrigger.gui.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;
|
||||
@@ -468,22 +786,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);
|
||||
|
||||
@@ -7,9 +7,14 @@ import static net.minecraft.util.StatCollector.translateToLocalFormatted;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagInt;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
@@ -17,25 +22,83 @@ import org.lwjgl.input.Keyboard;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
|
||||
public class ItemConstructableTrigger extends Item {
|
||||
|
||||
private static final String TAG_MODE = "mode";
|
||||
|
||||
private static final IIcon[] textures = new IIcon[3];
|
||||
|
||||
public ItemConstructableTrigger() {
|
||||
setUnlocalizedName("structurelib.constructableTrigger");
|
||||
setTextureName(MOD_ID + ":itemConstructableTrigger");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
public static void cycleMode(ItemStack held) {
|
||||
if (held.getTagCompound() == null) held.setTagCompound(new NBTTagCompound());
|
||||
if (!held.getTagCompound().hasKey(TAG_MODE)) held.getTagCompound().setTag(TAG_MODE, new NBTTagInt(0));
|
||||
held.getTagCompound().setInteger(TAG_MODE, (held.getTagCompound().getInteger(TAG_MODE) + 1) % 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerIcons(IIconRegister register) {
|
||||
textures[0] = register.registerIcon(MOD_ID + ":constructableTrigger/BUILDING");
|
||||
textures[1] = register.registerIcon(MOD_ID + ":constructableTrigger/UPDATING");
|
||||
textures[2] = register.registerIcon(MOD_ID + ":constructableTrigger/REMOVING");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(ItemStack stack, int renderPass, EntityPlayer player, ItemStack usingItem, int useRemaining) {
|
||||
return getIcon(stack, renderPass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(ItemStack stack, int pass) {
|
||||
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
|
||||
return textures[0];
|
||||
}
|
||||
return textures[stack.getTagCompound().getInteger(TAG_MODE)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemStackDisplayName(ItemStack stack) {
|
||||
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
|
||||
return getDisplayName(0);
|
||||
}
|
||||
return getDisplayName(stack.getTagCompound().getInteger(TAG_MODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIconIndex(ItemStack stack) {
|
||||
return getIcon(stack, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIconFromDamage(int damage) {
|
||||
return textures[damage];
|
||||
}
|
||||
|
||||
private String getDisplayName(int mode) {
|
||||
return StatCollector.translateToLocal("item.structurelib.constructableTrigger.name") + " - "
|
||||
+ StatCollector.translateToLocal("item.structurelib.constructableTrigger.modes." + mode);
|
||||
}
|
||||
|
||||
public static int getMode(ItemStack held) {
|
||||
if (held == null || !held.hasTagCompound() || !held.getTagCompound().hasKey(TAG_MODE)) {
|
||||
// default behavior
|
||||
return 0;
|
||||
}
|
||||
// get user-defined mode
|
||||
return held.getTagCompound().getInteger(TAG_MODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (!world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
} else {
|
||||
FMLCommonHandler.instance().showGuiScreen(new GuiScreenConfigureChannels(stack));
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class StructureProjectorCycleModeMessage implements IMessage {
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
// no op
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<StructureProjectorCycleModeMessage, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(StructureProjectorCycleModeMessage message, MessageContext ctx) {
|
||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||
final ItemStack held = player.getHeldItem();
|
||||
if (held != null && held.getItem() instanceof ItemConstructableTrigger) {
|
||||
ItemConstructableTrigger.cycleMode(held);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,77 +348,47 @@ public interface IStructureDefinition<T> {
|
||||
}
|
||||
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||
boolean success;
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
|
||||
"check");
|
||||
} else {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||
"check force");
|
||||
}
|
||||
boolean success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
checkBlocksIfNotNullForceCheckAllIfTrue ? (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)
|
||||
: skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||
checkBlocksIfNotNullForceCheckAllIfTrue ? "check" : "check force");
|
||||
if (StructureLibAPI.isDebugEnabled() && success) {
|
||||
StructureLib.LOGGER
|
||||
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
|
||||
}
|
||||
return success;
|
||||
} else {
|
||||
if (hintsOnly) {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.spawnHint(object, world, x, y, z, trigger);
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, "show_error")
|
||||
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
"spawnHint");
|
||||
} else {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.placeBlock(object, world, x, y, z, trigger);
|
||||
return true;
|
||||
}),
|
||||
"placeBlock");
|
||||
}
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
hintsOnly ? ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.spawnHint(object, world, x, y, z, trigger);
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
|
||||
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||
}
|
||||
return true;
|
||||
}) : ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.placeBlock(object, world, x, y, z, trigger);
|
||||
return true;
|
||||
}),
|
||||
hintsOnly ? "spawnHint" : "placeBlock");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
|
||||
@@ -137,12 +138,13 @@ public interface IStructureElement<T> {
|
||||
*/
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
IItemSource source = env.getSource();
|
||||
EntityPlayer actor = env.getActor();
|
||||
Consumer<IChatComponent> chatter = env.getChatter();
|
||||
if (e != null) {
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (e.getStacks() == null) {
|
||||
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
@@ -159,7 +161,7 @@ public interface IStructureElement<T> {
|
||||
chatter);
|
||||
}
|
||||
for (ItemStack stack : e.getStacks()) {
|
||||
if (!source.takeOne(stack, true)) continue;
|
||||
if (mode < 2 && !source.takeOne(stack, true)) continue;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
stack,
|
||||
NBTMode.EXACT,
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -51,6 +52,7 @@ import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
||||
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
|
||||
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
|
||||
@@ -206,7 +208,11 @@ public class StructureUtility {
|
||||
|
||||
@Override
|
||||
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
|
||||
world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2);
|
||||
placeRemoveOrUpdate(
|
||||
trigger,
|
||||
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
|
||||
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -219,11 +225,16 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (check(o, world, x, y, z)) return PlaceResult.SKIP;
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && check(o, world, x, y, z)) return PlaceResult.SKIP;
|
||||
// user should place anything here.
|
||||
// maybe make this configurable, but for now we try to take some cobble from user
|
||||
if (env.getSource().takeOne(new ItemStack(Blocks.cobblestone), false)) {
|
||||
world.setBlock(x, y, z, Blocks.cobblestone, 0, 2);
|
||||
placeRemoveOrUpdate(
|
||||
trigger,
|
||||
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
|
||||
}
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
@@ -330,29 +341,75 @@ public class StructureUtility {
|
||||
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
||||
EntityPlayer actor, Consumer<IChatComponent> chatter) {
|
||||
if (block == null) throw new NullPointerException();
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
int mode = 0;
|
||||
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
|
||||
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
|
||||
// ...trigger instead of assuming default behavior
|
||||
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
|
||||
}
|
||||
if (mode == 0 && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (mode == 1 && world.getTileEntity(x, y, z) instanceof TileEntity) {
|
||||
// We're in updating mode and the block we're looking at might be important, so let's just leave it alone
|
||||
// since it's probably not something we want to replace with something else
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
Item itemBlock = Item.getItemFromBlock(block);
|
||||
int itemMeta = itemBlock instanceof ISpecialItemBlock
|
||||
? ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta)
|
||||
: meta;
|
||||
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (mode == 1 && !world.isAirBlock(x, y, z)) {
|
||||
Block blockInWorld = world.getBlock(x, y, z);
|
||||
if (blockInWorld.isAssociatedBlock(block) && blockInWorld.getDamageValue(world, x, y, z) == meta) {
|
||||
// This is the block we are looking for, move along
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
}
|
||||
if (mode < 2 && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_simple_block",
|
||||
stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
|
||||
blockCustom.setBlock(world, x, y, z, meta);
|
||||
} else if (!stack.copy()
|
||||
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
|
||||
if (mode < 2) {
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
|
||||
blockCustom.setBlock(world, x, y, z, meta);
|
||||
} else {
|
||||
if (mode == 1 && !world.isAirBlock(x, y, z)) {
|
||||
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
|
||||
// ...and drop it to the player
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We either weren't able to give the player the block that was there, or we weren't able to
|
||||
// remove
|
||||
// the block, either way, we need to reject the update. Also, since we hid the block update from
|
||||
// the client in an effort to make the exchange as seamless as possible, we need to now notify
|
||||
// the client if we did successfully remove the block.
|
||||
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
if (!stack.copy()
|
||||
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!s.takeOne(stack, false)) {
|
||||
// rollback
|
||||
}
|
||||
}
|
||||
if (mode == 2 && world.isAirBlock(x, y, z)) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
if (mode == 2 || !s.takeOne(stack, false)) {
|
||||
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
world.setBlockToAir(x, y, z);
|
||||
if (mode == 2 && !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// Something went wrong while trying to give the player the block we removed, let's just fail for now
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
@@ -444,24 +501,74 @@ public class StructureUtility {
|
||||
if (stack == null) throw new NullPointerException();
|
||||
if (stack.stackSize != 1) throw new IllegalArgumentException();
|
||||
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
int mode = 0;
|
||||
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
|
||||
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
|
||||
// ...trigger instead of assuming default behavior
|
||||
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
|
||||
}
|
||||
if (mode == 1 && !world.isAirBlock(x, y, z)) {
|
||||
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
|
||||
// We're in updating mode and the block we're looking at might be important, so let's just leave it
|
||||
// alone
|
||||
// since it's probably not something we want to replace with something else
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
Block blockInWorld = world.getBlock(x, y, z);
|
||||
ItemStack blockInWorldItem = new ItemStack(blockInWorld);
|
||||
blockInWorldItem.setItemDamage(blockInWorld.getDamageValue(world, x, y, z));
|
||||
if (blockInWorldItem.isItemEqual(stack)) {
|
||||
// This is the block we are looking for, move along
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
}
|
||||
if (mode == 0 && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (mode < 2 && !assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation("structurelib.autoplace.error.no_item_stack", stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
|
||||
return PlaceResult.REJECT;
|
||||
if (!s.takeOne(stack, false))
|
||||
// this is bad! probably an exploit somehow. Let's nullify the block we just placed instead
|
||||
if (mode < 2) {
|
||||
if (mode == 1 && !world.isAirBlock(x, y, z)) {
|
||||
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
|
||||
// ...and drop it to the player
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We either weren't able to give the player the block that was there, or we weren't able to remove
|
||||
// the block, either way, we need to reject the update. Also, since we hid the block update from
|
||||
// the client in an effort to make the exchange as seamless as possible, we need to now notify
|
||||
// the client if we did successfully remove the block.
|
||||
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
if (!stack.copy()
|
||||
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
if (mode == 2 && world.isAirBlock(x, y, z)) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
if (mode == 2 || !s.takeOne(stack, false)) {
|
||||
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
world.setBlockToAir(x, y, z);
|
||||
if (mode == 2 && !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We were in removing mode and something went wrong while trying to give the player the block we
|
||||
// ...removed, let's just fail for now
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a structure element that only allow air (or air equivalents, like Railcraft hidden blocks). You usually
|
||||
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nontheless in case
|
||||
* you want this as a fallback to something else.
|
||||
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nonetheless in
|
||||
* case you want this as a fallback to something else.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> IStructureElement<T> isAir() {
|
||||
@@ -470,8 +577,8 @@ public class StructureUtility {
|
||||
|
||||
/**
|
||||
* Return a structure element that allow anything but air (or air equivalents, like Railcraft hidden blocks). You
|
||||
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided nontheless
|
||||
* in case you want this as a fallback to something else.
|
||||
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided
|
||||
* nonetheless in case you want this as a fallback to something else.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> IStructureElement<T> notAir() {
|
||||
@@ -614,46 +721,33 @@ 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:
|
||||
* <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>at 2nd, 3rd..., same thing in 2 happens
|
||||
* <li>at last occurrence, player placed a valid block with a tier not notSet. ofBlocksTiered now sets your current
|
||||
* tier to this tier, and accept the block
|
||||
* <li>check got notified structure is sound, and tier is not notSet. it marks the structure as complete and start
|
||||
* doing its business
|
||||
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
|
||||
* </ul>
|
||||
* (*): 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>
|
||||
* <b>Example:</b>
|
||||
* <b>Example Implementation:</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 {
|
||||
@@ -677,6 +771,29 @@ public class StructureUtility {
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Important bits:
|
||||
* <ul>
|
||||
* <li>The supplied ITierConverter returns null for an invalid block</li>
|
||||
* <li>Possible tier returned by ITierConverter is Integer object 0 to 15.</li>
|
||||
* <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>
|
||||
* <p>
|
||||
* <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this chain of events:
|
||||
* <ul>
|
||||
* <li>at check start, tier is reset to notSet
|
||||
* <li>the tier extractor returns notSet for first block, so since ofBlocksTiered finds your current tier is notSet,
|
||||
* and the new block's tier is the same as that, so it's accepted
|
||||
* <li>at 2nd, 3rd..., same thing in 2 happens
|
||||
* <li>at last occurrence, player placed a valid block with a tier not notSet. ofBlocksTiered now sets your current
|
||||
* tier to this tier, and accept the block
|
||||
* <li>check got notified structure is sound, and tier is not notSet. it marks the structure as complete and start
|
||||
* doing its business
|
||||
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
|
||||
* </ul>
|
||||
*
|
||||
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
|
||||
* be passed a null block or an invalid block meta. If this function returns null, then the
|
||||
* block would be considered invalid.
|
||||
@@ -686,8 +803,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
|
||||
*/
|
||||
@@ -697,7 +814,7 @@ public class StructureUtility {
|
||||
List<Pair<Block, Integer>> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers;
|
||||
if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException();
|
||||
IStructureElementCheckOnly<T> check = ofBlocksTiered(tierExtractor, notSet, setter, getter);
|
||||
return new StructureElement_Bridge<T>() {
|
||||
return new StructureElement_Bridge<>() {
|
||||
|
||||
@Override
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
@@ -754,12 +871,13 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
|
||||
Block block = world.getBlock(x, y, z);
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
TIER tier = tierExtractor.convert(block, meta);
|
||||
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
|
||||
if (mode < 2 && Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
|
||||
return PlaceResult.SKIP;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
hint.getKey(),
|
||||
@@ -849,7 +967,8 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (getBlock() == null) return PlaceResult.REJECT;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
getBlock(),
|
||||
@@ -936,7 +1055,8 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (init()) return StructureUtility.survivalPlaceBlock(block, meta, world, x, y, z, s, actor, chatter);
|
||||
return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
||||
}
|
||||
@@ -944,7 +1064,8 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (init()) return StructureUtility.survivalPlaceBlock(
|
||||
block,
|
||||
meta,
|
||||
@@ -1572,7 +1693,9 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && world.getBlock(x, y, z) == defaultBlock
|
||||
&& world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
return PlaceResult.SKIP;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
defaultBlock,
|
||||
@@ -1618,7 +1741,9 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode < 2 && world.getBlock(x, y, z) == defaultBlock
|
||||
&& world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
return PlaceResult.SKIP;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
defaultBlock,
|
||||
@@ -1878,7 +2003,8 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
if (predicate.test(t))
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode == 2 || predicate.test(t))
|
||||
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
||||
return placeResultWhenDisabled;
|
||||
}
|
||||
@@ -1886,7 +2012,9 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode == 2 || predicate.test(t))
|
||||
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
return placeResultWhenDisabled;
|
||||
}
|
||||
};
|
||||
@@ -3253,4 +3381,35 @@ public class StructureUtility {
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a creation, removal, or update operation at the given position
|
||||
*
|
||||
* @param trigger ItemStack for the trigger item
|
||||
* @param onCreate Operation to be performed when the mode of the trigger is set to building mode
|
||||
* @param onUpdate Operation to be performed when the mode of the trigger is set to updating mode
|
||||
* @param onRemove Operation to be performed when the mode of the trigger is set to removing mode
|
||||
*/
|
||||
public static void placeRemoveOrUpdate(ItemStack trigger, Runnable onCreate, Runnable onUpdate, Runnable onRemove) {
|
||||
int mode = ItemConstructableTrigger.getMode(trigger);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
onCreate.run();
|
||||
break;
|
||||
case 1:
|
||||
onUpdate.run();
|
||||
break;
|
||||
case 2:
|
||||
onRemove.run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean tryGiveOrDropItem(World world, EntityPlayer actor, ItemStack stack) {
|
||||
if (actor.inventory.addItemStackToInventory(stack)) {
|
||||
actor.inventoryContainer.detectAndSendChanges();
|
||||
return true;
|
||||
}
|
||||
return world.spawnEntityInWorld(new EntityItem(world, actor.posX, actor.posY, actor.posZ, stack));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -22,6 +22,9 @@ item.structurelib.frontRotationTool.desc.1=Rotates only the front panel,
|
||||
item.structurelib.frontRotationTool.desc.2=which allows structure rotation.
|
||||
|
||||
item.structurelib.constructableTrigger.name=Multiblock Structure Hologram Projector
|
||||
item.structurelib.constructableTrigger.modes.0=Building Mode
|
||||
item.structurelib.constructableTrigger.modes.1=Updating Mode
|
||||
item.structurelib.constructableTrigger.modes.2=Removing Mode
|
||||
item.structurelib.constructableTrigger.desc.0=Triggers Constructable Interface
|
||||
item.structurelib.constructableTrigger.desc.1=Shows multiblock construction schematic,
|
||||
item.structurelib.constructableTrigger.desc.2=use on the multiblock controller.
|
||||
@@ -40,6 +43,13 @@ item.structurelib.constructableTrigger.gui.error.show=Show Errors
|
||||
item.structurelib.constructableTrigger.gui.error.hide=Hide Errors
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.enable=No Hatch
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.disable=Hatches
|
||||
item.structurelib.constructableTrigger.keyCategory=Multiblock Structure Hologram Projector Keys
|
||||
item.structurelib.constructableTrigger.keys.cycleMode=Cycle Projector Mode
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
|
||||
|
||||
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.autoplace.built_stat=Built %s blocks
|
||||
structurelib.autoplace.complete=§aThis multiblock has completed!
|
||||
|
||||
@@ -36,10 +36,11 @@ 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.error.show=显示结构错误
|
||||
item.structurelib.constructableTrigger.gui.error.hide=隐藏结构错误
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.enable=跳过舱室
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.disable=放置舱室
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§r模组: §9§o%s
|
||||
|
||||
channels.structurelib.show_errors=添加后,在投影时会高亮标出必然错误的部分
|
||||
|
||||
structurelib.tooltip.channelvalue=§7表示子信道 §6%2$s§r§7 为 §6%d§r
|
||||
|
||||
structurelib.autoplace.built_stat=已放置 %s 个方块
|
||||
structurelib.autoplace.complete=§a这个多方块结构已经完成自动搭建!
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 6.6 KiB |
File renamed without changes.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 821 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 724 B |
Reference in new issue
Block a user