mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-09 15:04:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fdea65537 | ||
|
|
89057ebb2d | ||
|
|
d829ae466b | ||
|
|
137f651486 | ||
|
|
ff88cbf2d7 | ||
|
|
4922760b46 | ||
|
|
45bd25401f |
No files matched your search
+2
-3
@@ -1,9 +1,8 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.18:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.104-GTNH:dev")
|
||||
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.21:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev")
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
|
||||
/**
|
||||
* Handler for middle-click channel picking. When the player holds a hologram projector on the cursor and middle-clicks
|
||||
* a registered indicator item, the corresponding channel is set on the projector. The same applies in world when the
|
||||
* projector is held in hand and a registered indicator block is middle-clicked. Shift+middle-click wipes all channels.
|
||||
* Was made side neutral to support server-side validation at some point if necessary.
|
||||
*/
|
||||
public final class ChannelPickHandler {
|
||||
|
||||
private ChannelPickHandler() {}
|
||||
|
||||
/**
|
||||
* Handle a middle-click event while holding a hologram projector on the cursor.
|
||||
*/
|
||||
public static boolean handleMiddleClick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||
final boolean isSneaking) {
|
||||
return handlePick(player, holoStack, targetStack, isSneaking, true);
|
||||
}
|
||||
|
||||
public static boolean handleWorldPick(World world, EntityPlayer player, ItemStack holoStack,
|
||||
@Nullable MovingObjectPosition hit, final boolean isSneaking) {
|
||||
ItemStack targetStack = null;
|
||||
|
||||
if (hit != null) {
|
||||
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
|
||||
if (!block.isAir(world, hit.blockX, hit.blockY, hit.blockZ)) {
|
||||
targetStack = block.getPickBlock(hit, world, hit.blockX, hit.blockY, hit.blockZ, player);
|
||||
}
|
||||
}
|
||||
|
||||
// A null target still goes through so that shift+middle-click wipes even when aiming at nothing
|
||||
return handlePick(player, holoStack, targetStack, isSneaking, false);
|
||||
}
|
||||
|
||||
private static boolean handlePick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||
final boolean isSneaking, final boolean onCursor) {
|
||||
// Clear channels on shift+middle-click. The "press again to confirm" gesture is gated client-side
|
||||
// (see ChannelWipeConfirmation) before this method is ever called.
|
||||
if (isSneaking) {
|
||||
ChannelDataAccessor.wipeChannelData(holoStack);
|
||||
sendChannelData(player, holoStack, onCursor);
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.cleared"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetStack == null) return false; // nothing hovered, cancel silently
|
||||
|
||||
Collection<Map.Entry<String, Integer>> channels = ChannelDescription.iterate(targetStack);
|
||||
if (channels.isEmpty()) {
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.nomapping"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assuming there's only 1 channel per item for now (same behavior as drag and drop)
|
||||
Map.Entry<String, Integer> first = channels.iterator().next();
|
||||
ChannelDataAccessor.setChannelData(holoStack, first.getKey(), first.getValue());
|
||||
sendChannelData(player, holoStack, onCursor);
|
||||
player.addChatMessage(
|
||||
new ChatComponentTranslation("structurelib.pickchannel.success", first.getKey(), first.getValue()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is the one triggering this update, so should already be in-sync.
|
||||
*/
|
||||
private static void sendChannelData(EntityPlayer player, ItemStack holoStack, boolean onCursor) {
|
||||
if (!(player instanceof EntityPlayerMP)) {
|
||||
StructureLib.net.sendToServer(new SetChannelDataMessage(holoStack, onCursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
/**
|
||||
* Client-side-only confirmation gate for "wipe all channels" shift+middle-click gesture.
|
||||
*/
|
||||
public final class ChannelWipeConfirmation {
|
||||
|
||||
private static final long CONFIRM_WINDOW_MS = 5000;
|
||||
private static long armedAtMs = -1;
|
||||
|
||||
private ChannelWipeConfirmation() {}
|
||||
|
||||
/**
|
||||
* Call this before invoking {@link ChannelPickHandler} whenever the wipe gesture (shift+middle-click) is detected.
|
||||
*
|
||||
* @return true if this is a confirming second press within the window and the wipe should proceed now; false if
|
||||
* this press only armed the confirmation.
|
||||
*/
|
||||
public static boolean confirm(EntityPlayer player) {
|
||||
long now = Minecraft.getSystemTime();
|
||||
if (now - armedAtMs > CONFIRM_WINDOW_MS) {
|
||||
armedAtMs = now;
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.confirmwipe"));
|
||||
return false;
|
||||
}
|
||||
armedAtMs = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
@@ -40,6 +41,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
|
||||
@@ -53,6 +55,8 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
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 {
|
||||
|
||||
@@ -560,6 +564,35 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
public static class ForgeEventHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void onMouseEvent(MouseEvent event) {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
|
||||
if (player == null || player.isDead) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemStack heldItem = player.getHeldItem();
|
||||
|
||||
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) return;
|
||||
|
||||
if (event.button != 2 || !event.buttonstate) return;
|
||||
|
||||
// Sink the middle click as soon as a projector is held, so vanilla pick block does not run
|
||||
event.setCanceled(true);
|
||||
|
||||
final boolean isSneaking = player.isSneaking();
|
||||
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||
|
||||
ChannelPickHandler.handleWorldPick(
|
||||
player.worldObj,
|
||||
player,
|
||||
heldItem,
|
||||
Minecraft.getMinecraft().objectMouseOver,
|
||||
isSneaking);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
|
||||
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
@@ -128,6 +129,11 @@ public class StructureLib {
|
||||
DevelopHelper.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent e) {
|
||||
proxy.init(e);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||
proxy.loadComplete(e);
|
||||
|
||||
+6
-1
@@ -146,7 +146,12 @@ public class ChannelDataAccessor {
|
||||
*/
|
||||
public static void wipeChannelData(ItemStack masterStack) {
|
||||
if (masterStack == null) throw new IllegalArgumentException();
|
||||
if (masterStack.stackTagCompound != null) masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
||||
if (masterStack.stackTagCompound != null) {
|
||||
masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
||||
if (masterStack.stackTagCompound.hasNoTags()) {
|
||||
masterStack.stackTagCompound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizon.structurelib.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -7,7 +8,9 @@ import java.util.Set;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
@@ -44,6 +47,11 @@ public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
|
||||
List<String> mixins = new ArrayList<>(
|
||||
Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld"));
|
||||
if (FMLLaunchHandler.side() == Side.CLIENT) {
|
||||
mixins.add("MixinGuiContainer_PickChannel");
|
||||
}
|
||||
return mixins;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,9 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
private GuiTextField key, value;
|
||||
protected int guiTop, guiLeft;
|
||||
private List<String> info;
|
||||
private boolean wipePending = false;
|
||||
private long wipePendingTime = 0;
|
||||
private static final long WIPE_CONFIRM_TIMEOUT_MS = 3000;
|
||||
|
||||
public GuiScreenConfigureChannels(Container container, ItemStack trigger) {
|
||||
super(container);
|
||||
@@ -346,9 +349,17 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||
break;
|
||||
case WIPE_BTN:
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
key.setText("");
|
||||
this.value.setText("");
|
||||
if (!wipePending) {
|
||||
wipePending = true;
|
||||
wipePendingTime = System.currentTimeMillis();
|
||||
btn.displayString = I18n.format(I18N_PREFIX + "wipe.confirm");
|
||||
} else {
|
||||
wipePending = false;
|
||||
btn.displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
key.setText("");
|
||||
this.value.setText("");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -369,6 +380,10 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
super.updateScreen();
|
||||
key.updateCursorCounter();
|
||||
value.updateCursorCounter();
|
||||
if (wipePending && System.currentTimeMillis() - wipePendingTime > WIPE_CONFIRM_TIMEOUT_MS) {
|
||||
wipePending = false;
|
||||
getButtonList().get(WIPE_BTN).displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizon.structurelib.mixins.early;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.gtnewhorizon.structurelib.ChannelPickHandler;
|
||||
import com.gtnewhorizon.structurelib.ChannelWipeConfirmation;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
@Mixin(value = GuiContainer.class, priority = 100)
|
||||
public abstract class MixinGuiContainer_PickChannel {
|
||||
|
||||
@Inject(method = "mouseClicked(III)V", at = @At("HEAD"), cancellable = true)
|
||||
private void slib$onMouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) {
|
||||
if (mouseButton != 2) return;
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
if (player == null) return;
|
||||
|
||||
ItemStack cursorStack = player.inventory.getItemStack();
|
||||
if (cursorStack == null || !(cursorStack.getItem() instanceof ItemConstructableTrigger)) return;
|
||||
|
||||
// Sink middle click as soon as projector is picked up
|
||||
ci.cancel();
|
||||
|
||||
ItemStack hoveredStack = null;
|
||||
try {
|
||||
Slot slot = slib$invokeGetSlotAtPosition(mouseX, mouseY);
|
||||
if (slot != null) {
|
||||
hoveredStack = slot.getStack();
|
||||
}
|
||||
|
||||
// try NEI
|
||||
if (hoveredStack == null && Loader.isModLoaded("NotEnoughItems") && !NEIClientConfig.isHidden()) {
|
||||
hoveredStack = GuiContainerManager.getStackMouseOver((GuiContainer) (Object) this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
StructureLib.LOGGER.error("Error while resolving hovered slot for channel pick", e);
|
||||
}
|
||||
|
||||
boolean isSneaking = GuiScreen.isShiftKeyDown();
|
||||
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||
|
||||
ChannelPickHandler.handleMiddleClick(player, cursorStack, hoveredStack, isSneaking);
|
||||
}
|
||||
|
||||
@Invoker("getSlotAtPosition")
|
||||
protected abstract Slot slib$invokeGetSlotAtPosition(int x, int y);
|
||||
}
|
||||
@@ -23,15 +23,22 @@ import io.netty.buffer.ByteBuf;
|
||||
public class SetChannelDataMessage implements IMessage {
|
||||
|
||||
private final List<Map.Entry<String, Integer>> data = new ArrayList<>();
|
||||
private boolean onCursor;
|
||||
|
||||
public SetChannelDataMessage() {}
|
||||
|
||||
public SetChannelDataMessage(ItemStack trigger) {
|
||||
this(trigger, false);
|
||||
}
|
||||
|
||||
public SetChannelDataMessage(ItemStack trigger, final boolean onCursor) {
|
||||
this.onCursor = onCursor;
|
||||
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
onCursor = buf.readBoolean();
|
||||
data.clear();
|
||||
int size = ByteBufUtils.readVarShort(buf);
|
||||
for (int i = 0; i < size; i++) {
|
||||
@@ -41,6 +48,7 @@ public class SetChannelDataMessage implements IMessage {
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
buf.writeBoolean(onCursor);
|
||||
ByteBufUtils.writeVarShort(buf, data.size());
|
||||
for (Entry<String, Integer> e : data) {
|
||||
ByteBufUtils.writeUTF8String(buf, e.getKey());
|
||||
@@ -53,11 +61,11 @@ public class SetChannelDataMessage implements IMessage {
|
||||
@Override
|
||||
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
|
||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||
ItemStack heldItem = player.getHeldItem();
|
||||
if (heldItem != null && heldItem.getItem() instanceof ItemConstructableTrigger) {
|
||||
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||
ItemStack target = message.onCursor ? player.inventory.getItemStack() : player.getHeldItem();
|
||||
if (target != null && target.getItem() instanceof ItemConstructableTrigger) {
|
||||
ChannelDataAccessor.wipeChannelData(target);
|
||||
for (Entry<String, Integer> e : message.data) {
|
||||
ChannelDataAccessor.setChannelData(heldItem, e.getKey(), e.getValue());
|
||||
ChannelDataAccessor.setChannelData(target, e.getKey(), e.getValue());
|
||||
}
|
||||
// since this is a set all channel request from the client, we would assume client already know
|
||||
// what this would look like on the client, so no sync
|
||||
@@ -65,7 +73,7 @@ public class SetChannelDataMessage implements IMessage {
|
||||
StructureLib.LOGGER.warn(
|
||||
"{} trying to set channel data on {}, which is not a hologram projector!",
|
||||
player.getUniqueID(),
|
||||
heldItem);
|
||||
target);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import javax.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.event.HoverEvent;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
@@ -38,10 +39,9 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.ChatStyle;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
@@ -317,6 +317,25 @@ public class StructureUtility {
|
||||
return survivalPlaceBlock(block, meta, world, x, y, z, s, (EntityPlayer) actor, chatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a chat component naming the given stack, so that the receiving client localizes it.
|
||||
* <p>
|
||||
* Falls back to the server side display name for stacks whose name is not a plain translation key, e.g. items that
|
||||
* assemble their name in {@link Item#getItemStackDisplayName(ItemStack)}.
|
||||
*/
|
||||
private static IChatComponent getStackChatName(ItemStack stack) {
|
||||
String key = stack.getUnlocalizedName() + ".name";
|
||||
if (!StatCollector.canTranslate(key)) return stack.func_151000_E();
|
||||
IChatComponent name = new ChatComponentText("[").appendSibling(new ChatComponentTranslation(key))
|
||||
.appendText("]");
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
stack.writeToNBT(tag);
|
||||
name.getChatStyle()
|
||||
.setChatHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new ChatComponentText(tag.toString())))
|
||||
.setColor(stack.getRarity().rarityColor);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method for implementing
|
||||
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
||||
@@ -341,7 +360,7 @@ public class StructureUtility {
|
||||
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", getStackChatName(stack)));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting blockCustom) {
|
||||
@@ -447,7 +466,7 @@ public class StructureUtility {
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", getStackChatName(stack)));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
|
||||
@@ -1484,10 +1503,10 @@ public class StructureUtility {
|
||||
}
|
||||
for (ItemStack stack : e.getStacks()) {
|
||||
if (!source.takeOne(stack, true)) {
|
||||
IChatComponent name = new ChatComponentText(stack.getDisplayName());
|
||||
name.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW));
|
||||
env.getChatter()
|
||||
.accept(new ChatComponentTranslation("structurelib.autoplace.missing_block", name));
|
||||
env.getChatter().accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.missing_block",
|
||||
getStackChatName(stack)));
|
||||
continue;
|
||||
}
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
@@ -3013,7 +3032,8 @@ public class StructureUtility {
|
||||
@Override
|
||||
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
return backing.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
return backing
|
||||
.getBlocksToPlace(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel), env);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
||||
@@ -37,13 +37,18 @@ item.structurelib.constructableTrigger.gui.add=Add
|
||||
item.structurelib.constructableTrigger.gui.set=Update
|
||||
item.structurelib.constructableTrigger.gui.unset=Remove
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Clear
|
||||
item.structurelib.constructableTrigger.gui.wipe.confirm=§4Sure?
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
|
||||
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in their structure and a sub channel allows indication of which variant to use.\n All sub channels have a sequence of lowercase characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\n You can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of "§oIndicates§r §ovalue§r §oX§r §ofor§r §osub§r §ochannel§r §oY§r".
|
||||
|
||||
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
|
||||
|
||||
structurelib.tooltip.channelvalue=§7Indicates value §6%d§r§7 for sub channel §6%s
|
||||
structurelib.tooltip.indicator_dnd=Drop into GUI to select this value for this channel
|
||||
structurelib.tooltip.channelvalue=§7Value §b%d§r§7 for subchannel §b%s
|
||||
structurelib.tooltip.indicator_dnd=Drop into GUI or middle-click to select this value for this channel
|
||||
structurelib.pickchannel.success=§aSet §b%s§r§a to §b%d§r
|
||||
structurelib.pickchannel.cleared=§aAll channels cleared§r
|
||||
structurelib.pickchannel.confirmwipe=§6Sneak+Middle-click again to clear all channels
|
||||
structurelib.pickchannel.nomapping=§cNo channel mapping found for this item
|
||||
|
||||
structurelib.autoplace.built_stat=Built %s blocks
|
||||
structurelib.autoplace.complete=§aThis multiblock has completed!
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
tile.structurelib.blockhint.name=Подсказка
|
||||
tile.structurelib.blockhint.0.name=Подсказка 1 точка
|
||||
tile.structurelib.blockhint.1.name=Подсказка 2 точки
|
||||
tile.structurelib.blockhint.2.name=Подсказка 3 точки
|
||||
tile.structurelib.blockhint.3.name=Подсказка 4 точки
|
||||
tile.structurelib.blockhint.4.name=Подсказка 5 точек
|
||||
tile.structurelib.blockhint.5.name=Подсказка 6 точек
|
||||
tile.structurelib.blockhint.6.name=Подсказка 7 точек
|
||||
tile.structurelib.blockhint.7.name=Подсказка 8 точек
|
||||
tile.structurelib.blockhint.8.name=Подсказка 9 точек
|
||||
tile.structurelib.blockhint.9.name=Подсказка 10 точек
|
||||
tile.structurelib.blockhint.10.name=Подсказка 11 точек
|
||||
tile.structurelib.blockhint.11.name=Подсказка 12 точек
|
||||
tile.structurelib.blockhint.12.name=Общая подсказка
|
||||
tile.structurelib.blockhint.13.name=Подсказка воздух
|
||||
tile.structurelib.blockhint.14.name=Подсказка нет воздуха
|
||||
tile.structurelib.blockhint.15.name=Подсказка ошибка
|
||||
|
||||
item.structurelib.frontRotationTool.name=Инструмент для поворота (Автономный)
|
||||
item.structurelib.frontRotationTool.desc.0=Открывает интерфейс поворота лицевой стороны
|
||||
item.structurelib.frontRotationTool.desc.1=Поворачивает только лицевую сторону,
|
||||
item.structurelib.frontRotationTool.desc.2=что позволяет повернуть всю структуру.
|
||||
|
||||
item.structurelib.constructableTrigger.name=Голограммный проектор мультиблоковой структуры
|
||||
item.structurelib.constructableTrigger.desc.0=Используется по интерфейсу контроллера
|
||||
item.structurelib.constructableTrigger.desc.1=Показывает схему постройки мультиблочной структуры,
|
||||
item.structurelib.constructableTrigger.desc.2=используется при нажатии по контроллеру
|
||||
item.structurelib.constructableTrigger.desc.3=(Shift+ПКМ для автоматической постройки)
|
||||
item.structurelib.constructableTrigger.desc.4=Количество проекторов в одном слоте влияет на тир/режим/тип постройки, если он не задан в GUI проектора
|
||||
item.structurelib.constructableTrigger.desc.5=Удерживайте §l[LSHIFT]§r для отображения данных канала
|
||||
item.structurelib.constructableTrigger.desc.lshift.0=Содержит %d подканал
|
||||
item.structurelib.constructableTrigger.too_fast=Подождите %d миллисекунд перед очередным автозаполнением...
|
||||
item.structurelib.constructableTrigger.gui.title=Подканалы
|
||||
item.structurelib.constructableTrigger.gui.key=Ключ:
|
||||
item.structurelib.constructableTrigger.gui.value=Значение:
|
||||
item.structurelib.constructableTrigger.gui.add=Добавить
|
||||
item.structurelib.constructableTrigger.gui.set=Обновить
|
||||
item.structurelib.constructableTrigger.gui.unset=Удалить
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Очистить
|
||||
item.structurelib.constructableTrigger.gui.wipe.confirm=§4Уверен?
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§rМод: §9§o%s
|
||||
item.structurelib.constructableTrigger.gui.info=Многие мультиблоки поддерживают различные вариации структуры. Субканал позволяет указать, какой именно вариант использовать.\nВсе субканалы в качестве ключа используют строку из строчных букв, а в качестве значения - положительное целое число. Для большинства известных субканалов доступно автодополнение при вводе.\nВы также можете перетаскивать предметы из NEI, чтобы задать значение соответствующего субканала. Все эти предметы имеют подсказку: "§oЗадаёт§r §oзначение§r §oX§r §oдля§r §oсубканала§r §oY§r"
|
||||
|
||||
channels.structurelib.show_errors=Если включено, подсвечивает элементы мультиблока с явными ошибками
|
||||
|
||||
structurelib.tooltip.channelvalue=§7Указывает значение §b%d§r§7 для подканала §b%s
|
||||
structurelib.tooltip.indicator_dnd=Перейдите в GUI, чтобы выбрать это значение для этого канала.
|
||||
|
||||
structurelib.autoplace.built_stat=Построено %s блоков
|
||||
structurelib.autoplace.complete=§aЭтот мультиблок собран!
|
||||
structurelib.autoplace.error.not_enabled=§cЭтот мультиблок не поддерживает автозаполнение в выживании!
|
||||
structurelib.autoplace.error.no_simple_block=§cПредлагалось разместить §r%s§c но ничего не нашлось
|
||||
structurelib.autoplace.error.no_item_stack=§cПредлагалось разместить блок используя §r%s§c но ничего не нашлось
|
||||
structurelib.autoplace.warning.no_explicit_channel=§6Эта структура зависит от несуществующего подканала §r%s§6. Вместо этого используйте главный канал.
|
||||
|
||||
structurelib.blockhint.desc.0=Помогает при постройке
|
||||
structurelib.blockhint.0.name=Подсказка 1 точка
|
||||
structurelib.blockhint.1.name=Подсказка 2 точки
|
||||
structurelib.blockhint.2.name=Подсказка 3 точки
|
||||
structurelib.blockhint.3.name=Подсказка 4 точки
|
||||
structurelib.blockhint.4.name=Подсказка 5 точек
|
||||
structurelib.blockhint.5.name=Подсказка 6 точек
|
||||
structurelib.blockhint.6.name=Подсказка 7 точек
|
||||
structurelib.blockhint.7.name=Подсказка 8 точек
|
||||
structurelib.blockhint.8.name=Подсказка 9 точек
|
||||
structurelib.blockhint.9.name=Подсказка 10 точек
|
||||
structurelib.blockhint.10.name=Подсказка 11 точек
|
||||
structurelib.blockhint.11.name=Подсказка 12 точек
|
||||
structurelib.blockhint.desc.1=Заполнитель конкретной группы.
|
||||
structurelib.blockhint.12.name=Общая подсказка
|
||||
structurelib.blockhint.desc.2=Общий заполнитель.
|
||||
structurelib.blockhint.13.name=Подсказка воздух
|
||||
structurelib.blockhint.desc.3=Убедитесь, что это содержит воздух.
|
||||
structurelib.blockhint.14.name=Подсказка нет воздуха
|
||||
structurelib.blockhint.desc.4=Убедитесь, что это не содержит воздух.
|
||||
structurelib.blockhint.15.name=Подсказка ошибка
|
||||
structurelib.blockhint.desc.5=ОШИБКА (а что вы ожидали?)
|
||||
itemGroup.structurelib=StructureLib
|
||||
|
||||
structurelib.facing.0=вниз нормальный нет
|
||||
structurelib.facing.1=вниз нормальный по горизонтали
|
||||
structurelib.facing.2=вниз нормальный по вертикали
|
||||
structurelib.facing.3=вниз нормальный оба
|
||||
structurelib.facing.4=вниз по часовой стрелке нет
|
||||
structurelib.facing.5=вниз по часовой стрелке по горизонтали
|
||||
structurelib.facing.6=вниз по часовой стрелке по вертикали
|
||||
structurelib.facing.7=вниз по часовой стрелке оба
|
||||
structurelib.facing.8=вниз перевернутый
|
||||
structurelib.facing.9=вниз перевернутый
|
||||
structurelib.facing.10=вниз перевернутый
|
||||
structurelib.facing.11=вниз перевернутый
|
||||
structurelib.facing.12=вниз против часовой стрелки
|
||||
structurelib.facing.13=вниз против часовой стрелки
|
||||
structurelib.facing.14=вниз против часовой стрелки
|
||||
structurelib.facing.15=вниз против часовой стрелки
|
||||
structurelib.facing.16=вверх нормальный нет
|
||||
structurelib.facing.17=вверх нормальный по горизонтали
|
||||
structurelib.facing.18=вверх нормальный по вертикали
|
||||
structurelib.facing.19=вверх нормальный оба
|
||||
structurelib.facing.20=вверх по часовой стрелке нет
|
||||
structurelib.facing.21=вверх по часовой стрелке по горизонтали
|
||||
structurelib.facing.22=вверх по часовой стрелке по вертикали
|
||||
structurelib.facing.23=вверх по часовой стрелке оба
|
||||
structurelib.facing.24=вверх перевернутый
|
||||
structurelib.facing.25=вверх перевернутый
|
||||
structurelib.facing.26=вверх перевернутый
|
||||
structurelib.facing.27=вверх перевернутый
|
||||
structurelib.facing.28=вверх против часовой стрелки
|
||||
structurelib.facing.29=вверх против часовой стрелки
|
||||
structurelib.facing.30=вверх против часовой стрелки
|
||||
structurelib.facing.31=вверх против часовой стрелки
|
||||
structurelib.facing.32=север нормальный нет
|
||||
structurelib.facing.33=север нормальный по горизонтали
|
||||
structurelib.facing.34=север нормальный по вертикали
|
||||
structurelib.facing.35=север нормальный оба
|
||||
structurelib.facing.36=север по часовой стрелке нет
|
||||
structurelib.facing.37=север по часовой стрелке по горизонтали
|
||||
structurelib.facing.38=север по часовой стрелке по вертикали
|
||||
structurelib.facing.39=север по часовой стрелке оба
|
||||
structurelib.facing.40=север перевернутый
|
||||
structurelib.facing.41=север перевернутый
|
||||
structurelib.facing.42=север перевернутый
|
||||
structurelib.facing.43=север перевернутый
|
||||
structurelib.facing.44=север против часовой стрелки
|
||||
structurelib.facing.45=север против часовой стрелки
|
||||
structurelib.facing.46=север против часовой стрелки
|
||||
structurelib.facing.47=север против часовой стрелки
|
||||
structurelib.facing.48=юг нормальный нет
|
||||
structurelib.facing.49=юг нормальный по горизонтали
|
||||
structurelib.facing.50=юг нормальный по вертикали
|
||||
structurelib.facing.51=юг нормальный оба
|
||||
structurelib.facing.52=юг по часовой стрелке нет
|
||||
structurelib.facing.53=юг по часовой стрелке по горизонтали
|
||||
structurelib.facing.54=юг по часовой стрелке по вертикали
|
||||
structurelib.facing.55=юг по часовой стрелке оба
|
||||
structurelib.facing.56=юг перевернутый
|
||||
structurelib.facing.57=юг перевернутый
|
||||
structurelib.facing.58=юг перевернутый
|
||||
structurelib.facing.59=юг перевернутый
|
||||
structurelib.facing.60=юг против часовой стрелки
|
||||
structurelib.facing.61=юг против часовой стрелки
|
||||
structurelib.facing.62=юг против часовой стрелки
|
||||
structurelib.facing.63=юг против часовой стрелки
|
||||
structurelib.facing.64=запад нормальный нет
|
||||
structurelib.facing.65=запад нормальный по горизонтали
|
||||
structurelib.facing.66=запад нормальный по вертикали
|
||||
structurelib.facing.67=запад нормальный оба
|
||||
structurelib.facing.68=запад по часовой стрелке нет
|
||||
structurelib.facing.69=запад по часовой стрелке по горизонтали
|
||||
structurelib.facing.70=запад по часовой стрелке по вертикали
|
||||
structurelib.facing.71=запад по часовой стрелке оба
|
||||
structurelib.facing.72=запад перевернутый
|
||||
structurelib.facing.73=запад перевернутый
|
||||
structurelib.facing.74=запад перевернутый
|
||||
structurelib.facing.75=запад перевернутый
|
||||
structurelib.facing.76=запад против часовой стрелки
|
||||
structurelib.facing.77=запад против часовой стрелки
|
||||
structurelib.facing.78=запад против часовой стрелки
|
||||
structurelib.facing.79=запад против часовой стрелки
|
||||
structurelib.facing.80=восток нормальный нет
|
||||
structurelib.facing.81=восток нормальный по горизонтали
|
||||
structurelib.facing.82=восток нормальный по вертикали
|
||||
structurelib.facing.83=восток нормальный оба
|
||||
structurelib.facing.84=восток по часовой стрелке нет
|
||||
structurelib.facing.85=восток по часовой стрелке по горизонтали
|
||||
structurelib.facing.86=восток по часовой стрелке по вертикали
|
||||
structurelib.facing.87=восток по часовой стрелке оба
|
||||
structurelib.facing.88=восток перевернутый
|
||||
structurelib.facing.89=восток перевернутый
|
||||
structurelib.facing.90=восток перевернутый
|
||||
structurelib.facing.91=восток перевернутый
|
||||
structurelib.facing.92=восток против часовой стрелки
|
||||
structurelib.facing.93=восток против часовой стрелки
|
||||
structurelib.facing.94=восток против часовой стрелки
|
||||
structurelib.facing.95=восток против часовой стрелки
|
||||
|
||||
structurelib.config.client=Настройки клиента
|
||||
structurelib.config.client.tooltip=Настройки, которые изменяют только внешний вид игры, но не то, как она функционирует.
|
||||
structurelib.config.client.hologram=Голограммные настройки
|
||||
structurelib.config.client.hologram.tooltip=Настройки, которые по умолчанию меняют способ проецирования голограммы в мир.
|
||||
structurelib.config.client.hologram.maxCoexisting=Макс. сосуществующих голограмм
|
||||
structurelib.config.client.hologram.maxCoexisting.tooltip=Количество голограмм, которые могут сосуществовать. Обычно все подсказки, сгенерированные одним щелчком ПКМ, принадлежат одной голограмме. Будет предпринята попытка удаления старой голограммы, когда будет проецироваться новая голограмма и этот лимит будет превышен.
|
||||
structurelib.config.client.hologram.removeColliding=Удалить сталкивающиеся голограммы
|
||||
structurelib.config.client.hologram.removeColliding.tooltip=Whether an attempt will be made to remove any existing hologram that collides with a new hologram.
|
||||
|
||||
structurelib.config.registries=Sorted Registries
|
||||
structurelib.config.registries.tooltip=Sorted Registry is how StructureLib internally keep track of certain features. Each individual elements may be reordered, disabled and enabled at user's command. Changes will be synced to server if needed.\nThis gui can also be accessed in game by shift right clicking air with Multiblock Structure Hologram Projector.
|
||||
structurelib.config.registries.stackextractors=Stack Extractors
|
||||
structurelib.config.registries.stackextractors.tooltip=Controls which inventory-from-stack functions are enabled during survival autoplace
|
||||
structurelib.config.registries.inventoryproviders=Источники инвентарей
|
||||
structurelib.config.registries.inventoryproviders.tooltip=Controls which inventories functions are enabled during survival autoplace
|
||||
|
||||
structurelib.configgui.enabled=Включённый элемент
|
||||
structurelib.configgui.disabled=Отключённый элемент
|
||||
structurelib.configgui.drag.tooltip=Перетащите эту кнопку, чтобы изменить порядок этого элемента.
|
||||
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory=Основной инвентарь игрока
|
||||
structurelib.sortedregistry.inventoryproviders.5000-main-inventory.tooltip=36 ячеек для предметов в вашем основном инвентаре. Слоты для брони исключены.
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory=Инвентарь сундука Эндера
|
||||
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory.tooltip=Соединяет на ваш инвентарь из ванильного сундука Эндера. Требуется не менее один "похожий на рюкзак Эндера" предмет в основном инвентаре для работы.
|
||||
@@ -42,7 +42,7 @@ item.structurelib.constructableTrigger.gui.info=许多多方块的结构都是
|
||||
|
||||
channels.structurelib.show_errors=添加后,在投影时会高亮标出必然错误的部分
|
||||
|
||||
structurelib.tooltip.channelvalue=§7表示子信道 §6%2$s§r§7 为 §6%d§r
|
||||
structurelib.tooltip.channelvalue=§7表示子信道 §b%2$s§r§7 为 §b%d§r
|
||||
structurelib.tooltip.indicator_dnd=拖入GUI中来选择该值
|
||||
|
||||
structurelib.autoplace.built_stat=已放置 %s 个方块
|
||||
|
||||
Reference in new issue
Block a user