Add a simplified version of MultiblockTemplate to display MechMB parts in the manual

Updated the text splitter with the changes from the manual rewrite, fixed some additional bugs
This commit is contained in:
malte0811 2018-06-02 21:22:26 +02:00
parent 7ebf25b3d5
commit 448489b01f
17 changed files with 387 additions and 87 deletions

View file

@ -41,6 +41,7 @@ package malte0811.industrialWires;
import malte0811.industrialWires.network.MessagePanelInteract;
import malte0811.industrialWires.network.MessageTileSyncIW;
import malte0811.industrialWires.util.CommandIW;
import malte0811.industrialWires.util.MultiblockTemplateManual;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
@ -68,6 +69,7 @@ package malte0811.industrialWires;
import java.util.List;
import static malte0811.industrialWires.blocks.wire.BlockTypes_IC2_Connector.*;
import static malte0811.industrialWires.converter.MechMBPart.EXAMPLE_MECHMB_LOC;
import static malte0811.industrialWires.wires.IC2Wiretype.*;
@Mod(modid = IndustrialWires.MODID, version = IndustrialWires.VERSION, dependencies = "required-after:immersiveengineering@[0.12-77,);after:ic2",
@ -223,6 +225,7 @@ public class IndustrialWires {
MultiblockHandler.registerMultiblock(MultiblockMarx.INSTANCE);
MultiblockMechMB.INSTANCE = new MultiblockMechMB();
MultiblockHandler.registerMultiblock(MultiblockMechMB.INSTANCE);
MultiblockHandler.registerMultiblock(new MultiblockTemplateManual(EXAMPLE_MECHMB_LOC));
packetHandler.registerMessage(MessageTileSyncIW.HandlerClient.class, MessageTileSyncIW.class, 0, Side.CLIENT);
packetHandler.registerMessage(MessagePanelInteract.HandlerServer.class, MessagePanelInteract.class, 1, Side.SERVER);

View file

@ -68,7 +68,7 @@ import static malte0811.industrialWires.util.NBTKeys.*;
})
public class TileEntityMechMB extends TileEntityIWMultiblock implements ITickable, ISyncReceiver,
IEnergySource, IEnergySink, IPlayerInteraction, IRedstoneOutput, IBlockBoundsDirectional {
private static final double DECAY_BASE = Math.exp(Math.log(.8) / (2 * 60 * 60 * 20));
private static final double DECAY_BASE = Math.exp(Math.log(.95) / (60 * 60 * 20));
public static final double TICK_ANGLE_PER_SPEED = 180 / 20 / Math.PI;
private static final double SYNC_THRESHOLD = .95;
public MechMBPart[] mechanical = null;

View file

@ -20,6 +20,7 @@ import blusunrize.immersiveengineering.api.energy.wires.WireApi;
import blusunrize.immersiveengineering.client.ClientUtils;
import blusunrize.immersiveengineering.common.Config;
import blusunrize.immersiveengineering.common.util.Utils;
import blusunrize.lib.manual.IManualPage;
import blusunrize.lib.manual.ManualInstance;
import blusunrize.lib.manual.ManualPages;
import blusunrize.lib.manual.ManualPages.PositionedItemStack;
@ -45,6 +46,7 @@ import malte0811.industrialWires.client.panelmodel.PanelModelLoader;
import malte0811.industrialWires.client.render.*;
import malte0811.industrialWires.controlpanel.PanelComponent;
import malte0811.industrialWires.converter.MechEnergy;
import malte0811.industrialWires.converter.MechMBPart;
import malte0811.industrialWires.crafting.IC2TRHelper;
import malte0811.industrialWires.entities.EntityBrokenPart;
import malte0811.industrialWires.hv.MarxOreHandler;
@ -52,6 +54,7 @@ import malte0811.industrialWires.hv.MultiblockMarx;
import malte0811.industrialWires.items.ItemIC2Coil;
import malte0811.industrialWires.items.ItemPanelComponent;
import malte0811.industrialWires.util.CommandIWClient;
import malte0811.industrialWires.util.MiscUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.audio.MovingSound;
@ -86,6 +89,7 @@ public class ClientProxy extends CommonProxy {
@Override
public void preInit() {
super.preInit();
if (IndustrialWires.hasIC2) {
WireApi.registerConnectorForRender("ic2_conn_tin", new ResourceLocation("immersiveengineering:block/connector/connector_lv.obj"),
ImmutableMap.of("#immersiveengineering:blocks/connector_connector_lv",
@ -260,16 +264,29 @@ public class ClientProxy extends CommonProxy {
boolean uni = m.fontRenderer.getUnicodeFlag();
m.fontRenderer.setUnicodeFlag(true);
m.entryRenderPre();
TextSplitter splitter = new TextSplitter(m.fontRenderer::getStringWidth, 16, 120,
(s) -> new ManualPages.Text(m, s));
TextSplitter splitter = new TextSplitter(m);
splitter.addSpecialPage(0, 0, 6,
(s) -> new ManualPageMultiblock(m, s,
MultiblockMarx.INSTANCE));
splitter.split(text);
m.entryRenderPost();
m.fontRenderer.setUnicodeFlag(uni);
List<ManualPages> marxEntry = splitter.toManualEntry();
m.addEntry("industrialwires.marx", IndustrialWires.MODID, marxEntry.toArray(new ManualPages[0]));
List<IManualPage> marxEntry = splitter.toManualEntry();
m.addEntry("industrialwires.marx", IndustrialWires.MODID, marxEntry.toArray(new IManualPage[0]));
text = I18n.format("ie.manual.entry.industrialwires.mech_mb");
splitter = new TextSplitter(m);
splitter.addSpecialPage(0, 0, 8, (s)->new ManualPageMultiblock(m, s,
MiscUtils.getMBFromName(MechMBPart.EXAMPLE_MECHMB_LOC.toString())));
uni = m.fontRenderer.getUnicodeFlag();
m.fontRenderer.setUnicodeFlag(true);
m.entryRenderPre();
splitter.split(text);
m.entryRenderPost();
m.fontRenderer.setUnicodeFlag(uni);
List<IManualPage> mechMBEntry = splitter.toManualEntry();
m.addEntry("industrialwires.mech_mb", IndustrialWires.MODID, mechMBEntry.toArray(new IManualPage[0]));
ClientCommandHandler.instance.registerCommand(new CommandIWClient());
}

View file

@ -15,7 +15,13 @@
package malte0811.industrialWires.client.manual;
import blusunrize.lib.manual.IManualPage;
import blusunrize.lib.manual.ManualInstance;
import blusunrize.lib.manual.ManualPages;
import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.util.ArrayList;
import java.util.HashMap;
@ -26,93 +32,125 @@ import java.util.stream.Collectors;
public class TextSplitter {
private final Function<String, Integer> width;
private final TIntObjectMap<Map<Integer, Page>> specialByAnchor = new TIntObjectHashMap<>();
private final TIntObjectMap<Page> specialByPage = new TIntObjectHashMap<>();
private final List<List<String>> entry = new ArrayList<>();
private final Function<String, String> tokenTransform;
private final int lineWidth;
private Map<Integer, Map<Integer, Page>> linesOnSpecialPages = new HashMap<>();
private Map<Integer, Page> pageToSpecial = new HashMap<>();
private List<List<String>> entry = new ArrayList<>();
private Page defPage;
public TextSplitter(Function<String, Integer> w, int lP, int lW, Function<String, ManualPages> defaultPage) {
private final int linesPerPage;
private TIntIntMap pageByAnchor = new TIntIntHashMap();
private final Function<String, IManualPage> defaultPage;
public TextSplitter(Function<String, Integer> w, int lineWidthPixel, int linesPerPage,
Function<String, IManualPage> defPage, Function<String, String> tokenTransform) {
width = w;
lineWidth = lW;
defPage = new Page(lP, defaultPage);
this.lineWidth = lineWidthPixel;
this.linesPerPage = linesPerPage;
this.tokenTransform = tokenTransform;
this.defaultPage = defPage;
}
public void clearSpecial() {
linesOnSpecialPages.clear();
public TextSplitter(ManualInstance m) {
this(m.fontRenderer::getStringWidth, 120, 16, s-> new ManualPages.Text(m, s), (s) -> s);
}
public void addSpecialPage(int ref, int offset, int linesOnPage, Function<String, ManualPages> factory) {
if (offset<0||(ref!=-1&&ref<0)) {
public TextSplitter(ManualInstance m, Function<String, String> tokenTransform) {
this(m.fontRenderer::getStringWidth, 120, 16,s-> new ManualPages.Text(m, s), tokenTransform);
}
public void clearSpecialByPage() {
specialByPage.clear();
}
public void clearSpecialByAnchor() {
specialByAnchor.clear();
}
public void addSpecialPage(int ref, int offset, int lines, Function<String, IManualPage> factory) {
if (offset < 0 || (ref != -1 && ref < 0)) {
throw new IllegalArgumentException();
}
if (!linesOnSpecialPages.containsKey(ref)) {
linesOnSpecialPages.put(ref, new HashMap<>());
if (!specialByAnchor.containsKey(ref)) {
specialByAnchor.put(ref, new HashMap<>());
}
linesOnSpecialPages.get(ref).put(offset, new Page(linesOnPage, factory));
specialByAnchor.get(ref).put(offset, new Page(lines, factory));
}
// I added labels to all break statements to make it more readable
@SuppressWarnings({"UnnecessaryLabelOnBreakStatement", "UnusedLabel"})
public void split(String in) {
clearSpecialByPage();
entry.clear();
String[] wordsAndSpaces = splitWhitespace(in);
int pos = 0;
List<String> overflow = new ArrayList<>();
updateSpecials(-1, 0);
entry:while (pos<wordsAndSpaces.length) {
List<String> page = new ArrayList<>();
page.addAll(overflow);
updateSpecials(-1, 0, 0);
entry:
while (pos < wordsAndSpaces.length) {
List<String> page = new ArrayList<>(overflow);
overflow.clear();
page:while (page.size()<getLinesOnPage(entry.size())&&pos<wordsAndSpaces.length) {
page:
while (page.size() < getLinesOnPage(entry.size()) && pos < wordsAndSpaces.length) {
String line = "";
int currWidth = 0;
line:while (pos<wordsAndSpaces.length&&currWidth<lineWidth) {
String text = wordsAndSpaces[pos];
if (pos<wordsAndSpaces.length) {
int textWidth = getWidth(text);
if (currWidth + textWidth < lineWidth||line.length()==0) {
pos++;
if (text.equals("<np>")) {
page.add(line);
break page;
} else if (text.equals("<br>")) {
break line;
} else if (text.startsWith("<&")&&text.endsWith(">")) {
int id = Integer.parseInt(text.substring(2, text.length()-1));
int pageForId = entry.size();
Map<Integer, Page> specialForId = linesOnSpecialPages.get(id);
if (specialForId!=null&&specialForId.containsKey(0)) {
if (page.size()>getLinesOnPage(pageForId)) {
pageForId++;
}
}
updateSpecials(id, pageForId);
} else if (!Character.isWhitespace(text.charAt(0))||line.length()!=0) {//Don't add whitespace at the start of a line
line += text;
currWidth += textWidth;
}
} else {
line:
while (pos < wordsAndSpaces.length && currWidth < lineWidth) {
String token = tokenTransform.apply(wordsAndSpaces[pos]);
int textWidth = getWidth(token);
if (currWidth + textWidth < lineWidth || line.length() == 0) {
pos++;
if (token.equals("<np>")) {
page.add(line);
break page;
} else if (token.equals("<br>")) {
break line;
} else if (token.startsWith("<&") && token.endsWith(">")) {
int id = Integer.parseInt(token.substring(2, token.length() - 1));
int pageForId = entry.size();
Map<Integer, Page> specialForId = specialByAnchor.get(id);
if (specialForId != null && specialForId.containsKey(0)) {
if (page.size() > getLinesOnPage(pageForId)) {
pageForId++;
}
}
//New page if there is already a special element on this page
if (updateSpecials(id, pageForId, page.size())) {
page.add(line);
pos--;
break page;
}
} else if (!Character.isWhitespace(token.charAt(0)) || line.length() != 0) {//Don't add whitespace at the start of a line
line += token;
currWidth += textWidth;
}
} else {
break line;
}
}
page.add(line);
line = line.trim();
if (!line.isEmpty())
page.add(line);
}
if (!page.stream().allMatch(String::isEmpty)) {
int linesMax = getLinesOnPage(entry.size());
if (page.size()>linesMax) {
if (page.size() > linesMax) {
overflow.addAll(page.subList(linesMax, page.size()));
page = page.subList(0, linesMax-1);
page = page.subList(0, linesMax);
}
entry.add(page);
}
}
}
public List<ManualPages> toManualEntry() {
List<ManualPages> ret = new ArrayList<>(entry.size());
public List<IManualPage> toManualEntry() {
List<IManualPage> ret = new ArrayList<>(entry.size());
for (int i = 0; i < entry.size(); i++) {
String s = entry.get(i).stream().collect(Collectors.joining("\n"));
ret.add(pageToSpecial.getOrDefault(i, defPage).factory.apply(s));
if (specialByPage.containsKey(i)) {
ret.add(specialByPage.get(i).factory.apply(s));
} else {
ret.add(defaultPage.apply(s));
}
}
return ret;
}
@ -132,81 +170,83 @@ public class TextSplitter {
}
private int getLinesOnPage(int id) {
if (pageToSpecial.containsKey(id)) {
return pageToSpecial.get(id).lines;
if (specialByPage.containsKey(id)) {
return specialByPage.get(id).lines;
}
return defPage.lines;
return linesPerPage;
}
private void updateSpecials(int ref, int page) {
if (linesOnSpecialPages.containsKey(ref)) {
for (Map.Entry<Integer, Page> entry :linesOnSpecialPages.get(ref).entrySet()) {
int specialPage = page+entry.getKey();
if (pageToSpecial.containsKey(specialPage)) {
throw new IllegalStateException("Page "+specialPage+" was registered already");
private boolean updateSpecials(int ref, int page, int currLine) {
if (specialByAnchor.containsKey(ref)) {
TIntObjectMap<Page> specialByPageTmp = new TIntObjectHashMap<>();
for (Map.Entry<Integer, Page> entry : specialByAnchor.get(ref).entrySet()) {
int specialPage = page + entry.getKey();
if (specialByPage.containsKey(specialPage)) {
return true;
}
pageToSpecial.put(specialPage, entry.getValue());
if (entry.getKey()==0&&entry.getValue().lines<=currLine) {
return true;
}
specialByPageTmp.put(specialPage, entry.getValue());
}
} else if (ref!=-1) {//Default reference for page 0
System.out.println("WARNING: Reference "+ref+" was found, but no special pages were registered for it");
specialByPage.putAll(specialByPageTmp);
} else if (ref != -1) {//Default reference for page 0
System.out.println("WARNING: Reference " + ref + " was found, but no special pages were registered for it");
}
pageByAnchor.put(ref, page);
return false;
}
private String[] splitWhitespace(String in) {
List<String> parts = new ArrayList<>();
for (int i = 0;i<in.length();) {
for (int i = 0; i < in.length(); ) {
StringBuilder here = new StringBuilder();
char first = in.charAt(i);
here.append(first);
i++;
for (;i<in.length();) {
for (; i < in.length(); ) {
char hereC = in.charAt(i);
byte action = shouldSplit(first, hereC);
if ((action&1)!=0) {
if ((action & 1) != 0) {
here.append(in.charAt(i));
i++;
}
if ((action&2)!=0||(action&1)==0) {
if ((action & 2) != 0 || (action & 1) == 0) {
break;
}
}
parts.add(here.toString());
}
return parts.toArray(new String[parts.size()]);
return parts.toArray(new String[0]);
}
/**
* @return
* &1: add
* @return &1: add
* &2: end here
*/
private byte shouldSplit(char start, char here) {
byte ret = 0b01;
if (Character.isWhitespace(start)^Character.isWhitespace(here)) {
if (Character.isWhitespace(start) ^ Character.isWhitespace(here)) {
ret = 0b10;
}
if (here=='<') {
if (here == '<') {
ret = 0b10;
}
if (start=='<') {
if (start == '<') {
ret = 0b01;
if (here=='>') {
if (here == '>') {
ret |= 0b10;
}
}
return ret;
}
public List<List<String>> getEntry() {
return entry;
}
private class Page {
final int lines;
final Function<String, ManualPages> factory;
public Page(int l, Function<String, ManualPages> f) {
final Function<String, IManualPage> factory;
public Page(int l, Function<String, IManualPage> f) {
factory = f;
lines = l;
}
}
}
}

View file

@ -15,6 +15,7 @@
package malte0811.industrialWires.converter;
import blusunrize.immersiveengineering.api.MultiblockHandler;
import blusunrize.immersiveengineering.common.IEContent;
import blusunrize.immersiveengineering.common.blocks.metal.BlockTypes_MetalDecoration0;
import blusunrize.immersiveengineering.common.util.Utils;
@ -28,6 +29,7 @@ import malte0811.industrialWires.client.render.TileRenderMechMB;
import malte0811.industrialWires.entities.EntityBrokenPart;
import malte0811.industrialWires.util.LocalSidedWorld;
import malte0811.industrialWires.util.MiscUtils;
import malte0811.industrialWires.util.MultiblockTemplateManual;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.init.Blocks;
@ -52,6 +54,7 @@ import java.util.function.Consumer;
import static blusunrize.immersiveengineering.common.IEContent.blockMetalDecoration0;
import static blusunrize.immersiveengineering.common.blocks.metal.BlockTypes_MetalDecoration0.HEAVY_ENGINEERING;
import static blusunrize.immersiveengineering.common.blocks.metal.BlockTypes_MetalDecoration0.LIGHT_ENGINEERING;
import static malte0811.industrialWires.IndustrialWires.MODID;
import static malte0811.industrialWires.blocks.converter.MechanicalMBBlockType.NO_MODEL;
import static malte0811.industrialWires.util.NBTKeys.TYPE;
@ -120,6 +123,7 @@ public abstract class MechMBPart {
public static final BiMap<String, Class<? extends MechMBPart>> REGISTRY = HashBiMap.create();
public static final String SHAFT_KEY = "shaft";
public static final ResourceLocation EXAMPLE_MECHMB_LOC = new ResourceLocation(MODID, "example_mech_mb");
public static final Comparator<MechMBPart> SORT_BY_COUNT = (a, b)-> {
if (a.getLength()!=b.getLength()) {
@ -147,9 +151,23 @@ public abstract class MechMBPart {
for (String key : REGISTRY.keySet()) {
cacheNewInstance(key);
MultiblockHandler.registerMultiblock(
new MultiblockTemplateManual(getSchematicLocationForPart(key)));
}
}
public static ResourceLocation getSchematicLocationForPart(Class<? extends MechMBPart> cl) {
String name = REGISTRY.inverse().get(cl);
return getSchematicLocationForPart(name);
}
public static ResourceLocation getSchematicLocationForPart(String name) {
if (name==null)
return null;
name = MiscUtils.toSnakeCase(name);
return new ResourceLocation(MODID, name);
}
public static void cacheNewInstance(String key) {
try {
MechMBPart instance = REGISTRY.get(key).newInstance();

View file

@ -17,18 +17,21 @@ package malte0811.industrialWires.util;
import blusunrize.immersiveengineering.ImmersiveEngineering;
import blusunrize.immersiveengineering.api.ApiUtils;
import blusunrize.immersiveengineering.api.MultiblockHandler;
import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable;
import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler;
import blusunrize.immersiveengineering.common.util.Utils;
import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4;
import com.google.common.collect.ImmutableSet;
import malte0811.industrialWires.IndustrialWires;
import malte0811.industrialWires.hv.MultiblockMarx;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.*;
import net.minecraft.world.World;
import net.minecraftforge.common.property.IExtendedBlockState;
@ -264,4 +267,63 @@ public final class MiscUtils {
}
return ret;
}
public static EnumFacing applyRotationToFacing(Rotation rot, EnumFacing facing)
{
switch(rot)
{
case CLOCKWISE_90:
facing = facing.rotateY();
break;
case CLOCKWISE_180:
facing = facing.getOpposite();
break;
case COUNTERCLOCKWISE_90:
facing = facing.rotateYCCW();
break;
}
return facing;
}
public static Rotation getRotationBetweenFacings(EnumFacing orig, EnumFacing to)
{
if (to==orig)
return Rotation.NONE;
if (orig.getAxis()==EnumFacing.Axis.Y||to.getAxis()==EnumFacing.Axis.Y)
return null;
orig = orig.rotateY();
if (orig==to)
return Rotation.CLOCKWISE_90;
orig = orig.rotateY();
if (orig==to)
return Rotation.CLOCKWISE_180;
orig = orig.rotateY();
if (orig==to)
return Rotation.COUNTERCLOCKWISE_90;
return null;//This shouldn't ever happen
}
public static String toSnakeCase(String in) {
StringBuilder ret = new StringBuilder(in.length());
ret.append(in.charAt(0));
for (int i = 1;i<in.length();i++) {
char here = in.charAt(i);
if (Character.isUpperCase(here)) {
ret.append('_').append(Character.toLowerCase(here));
} else {
ret.append(here);
}
}
return ret.toString();
}
public static MultiblockHandler.IMultiblock getMBFromName(String s) {
for (MultiblockHandler.IMultiblock mb:MultiblockHandler.getMultiblocks()) {
if (mb.getUniqueName().equals(s)) {
return mb;
}
}
return MultiblockMarx.INSTANCE;
}
}

View file

@ -0,0 +1,155 @@
/*
* This file is part of Industrial Wires.
* Copyright (C) 2016-2018 malte0811
* Industrial Wires is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* Industrial Wires is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Industrial Wires. If not, see <http://www.gnu.org/licenses/>.
*/
package malte0811.industrialWires.util;
import blusunrize.immersiveengineering.api.MultiblockHandler;
import blusunrize.immersiveengineering.api.crafting.IngredientStack;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.datafix.DataFixesManager;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import net.minecraft.world.gen.structure.template.Template;
import net.minecraft.world.gen.structure.template.TemplateManager;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.*;
//This is just for manual entries
public class MultiblockTemplateManual implements MultiblockHandler.IMultiblock {
public static final TemplateManager RES_LOC_TEMPLATE_MANAGER = new TemplateManager("/dev/null/should not exist",
DataFixesManager.createFixer());
private final ResourceLocation loc;
@Nullable
private Template template;
@Nullable
private IngredientStack[] mats = null;
@Nullable
private ItemStack[][][] fakeStructure = null;
@Nullable
private Map<ItemStack, IBlockState> realStructure = null;
public MultiblockTemplateManual(ResourceLocation loc) {
this.loc = loc;
}
private void updateTemplate() {
if (template == null)
{
template = RES_LOC_TEMPLATE_MANAGER.getTemplate(null, loc);
Vec3i size = template.getSize();
fakeStructure = new ItemStack[size.getY()][size.getX()][size.getZ()];
for (int x = 0; x < size.getX(); x++) {
for (int y = 0; y < size.getY(); y++) {
for (int z = 0; z < size.getZ(); z++) {
fakeStructure[y][x][z] = new ItemStack(Items.COMMAND_BLOCK_MINECART);
}
}
}
realStructure = new IdentityHashMap<>();
List<Template.BlockInfo> blocks = template.blocks;
Set<ItemStack> matsSet = new HashSet<>();
for (Template.BlockInfo info : blocks) {
ItemStack here = new ItemStack(info.blockState.getBlock(), 1,
info.blockState.getBlock().getMetaFromState(info.blockState));
if (!here.isEmpty()) {
realStructure.put(fakeStructure[info.pos.getY()][info.pos.getX()][info.pos.getZ()],
info.blockState);
Optional<ItemStack> match = matsSet.stream().filter(s -> ItemStack.areItemsEqual(here, s)).findAny();
if (match.isPresent()) {
match.get().grow(1);
} else {
matsSet.add(here);
}
}
}
mats = matsSet.stream().map(IngredientStack::new).toArray(IngredientStack[]::new);
for (int x = 0; x < size.getX(); x++) {
for (int y = 0; y < size.getY(); y++) {
for (int z = 0; z < size.getZ(); z++) {
if (!realStructure.containsKey(fakeStructure[y][x][z])) {
fakeStructure[y][x][z] = ItemStack.EMPTY;
}
}
}
}
}
}
@Override
public String getUniqueName() {
return loc.toString();
}
@Override
public boolean isBlockTrigger(IBlockState state) {
return false;
}
@Override
public boolean createStructure(World world, BlockPos pos, EnumFacing side, EntityPlayer player) {
return false;
}
@Override
public ItemStack[][][] getStructureManual() {
updateTemplate();
return fakeStructure;
}
@Override
public IBlockState getBlockstateFromStack(int index, ItemStack stack) {
updateTemplate();
assert realStructure != null;
return realStructure.getOrDefault(stack, Blocks.AIR.getDefaultState());
}
@Override
public IngredientStack[] getTotalMaterials() {
updateTemplate();
return mats;
}
@Override
public boolean overwriteBlockRender(ItemStack stack, int iterator) {
return false;
}
@Override
public float getManualScale() {
return 12;
}
@Override
@SideOnly(Side.CLIENT)
public boolean canRenderFormedStructure() {
return false;
}
@Override
@SideOnly(Side.CLIENT)
public void renderFormedStructure() {
}
}

View file

@ -0,0 +1 @@
public net.minecraft.world.gen.structure.template.Template field_186270_a #blocks

View file

@ -139,7 +139,11 @@ ie.manual.entry.industrialwires.jacobs1=These are the required power values in E
ie.manual.entry.industrialwires.marx.name=Marx Generator
ie.manual.entry.industrialwires.marx.subtext=I'm Erwin-Otto, not Karl!
ie.manual.entry.industrialwires.marx=A Marx Generator is a device use to produce high-voltage high-energy pulses. These pulses are visible as lightning between the output terminals and can be used to process ores. Each type of ore has an ideal amount of processing energy (see <link;industrialwires.marx;§oAppendix B§r;7>). The precise values are unknown, estimate values with 10%% accuracy can be found at the end of this entry. The factor between the actual value and the estimate is the same for all types of ore.<br>§lConstruction§r<&0><br>The above plan shows a 5-stage generator capable of producing 3 block lightning. An arbitrary amount of stages can be added by increasing the number of "middle" layers. Power (either IF or EU) is connected to the HV connector, the redstone wire for the control signals is connected to the redstone connector<br>§lEnergy§r<br>Each stage of the Marx generator consists of a 1.6μF capacitor that is charged to up to 250kV (see <link;industrialwires.marx;§oAppendix A§r;6>). When the generator is fully charged the voltage of each capacitor is roughly equal to the charging voltage. The total energy is the sum of the energy stored in the individual capacitors and is split equally between the ores to be processed.<br>§lControl signals§r<br>Voltages are represented by 2 signals: The first signal is simply proportional to the voltage to represent. The second signal is proportional to the voltage in the "gap" between 2 values of the first signal, thus allowing more precise control/measurements. Panel components capable of interacting with analog signals usually support this dual-channel setup. The charging voltage is controlled by the white and yellow signals. The voltages of the top and bottom capacitor are output to the magenta and pink resp. the orange and lime signals. The light blue signal is a firing control. If it is high the generator will attempt to fire. If the voltage of the bottom capacitor is lower than 125 kV or the total voltage is lower than 30%% of the maximum output voltage the generator will misfire, discharging the capacitors without actually producing lightning.<br>§lSafety§r<br>Due to the high voltages and energies involved in firing a Marx generator a safe distance should be maintained to avoid injury or death. Even outside of this area hearing protection (As provided by Immersive Engineering) is obligatory. Formulas to calculate the safe distances can be found in <link;industrialwires.marx;§oAppendix A§r;6>.<np>§lAppendix A: Formulas§r<br>Energy stored in a capacitor:<br>E=0.5*C*U^2<br>E: Energy, C: Capacitance, U: Voltage<br><br>Voltage from redstone signals:<br>U=250/255*(16*a+b)<br>U: Voltage (kV), a: First signal, b: Second signal<br><br>Safe distance (Physical damage):<br>r=sqrt(e/50,000)<br>r: Safe distance, e: Energy stored<br><br>Safe distance (Ear damage):<br>r=sqrt(e)/50<br>r: Safe distance, e: Energy stored<np>§lAppendix B: Ore Energy Values§r<br>
ie.manual.entry.industrialwires.marx=A Marx Generator is a device use to produce high-voltage high-energy pulses. These pulses are visible as lightning between the output terminals and can be used to process ores. Each type of ore has an ideal amount of processing energy (see <link;industrialwires.marx;§oAppendix B§r;7>). The precise values are unknown, estimate values with 10%% accuracy can be found at the end of this entry. The factor between the actual value and the estimate is the same for all types of ore.<br>§lConstruction§r<&0><br>The above plan shows a 5-stage generator capable of producing 3 block lightning. It is formed by hitting the side of the bottom left capacitor (The same side that the redstone connector is on) with an Engineer's hammer. An arbitrary amount of stages can be added by increasing the number of "middle" layers. Power (either IF or EU) is connected to the HV connector, the redstone wire for the control signals is connected to the redstone connector<br>§lEnergy§r<br>Each stage of the Marx generator consists of a 1.6μF capacitor that is charged to up to 250kV (see <link;industrialwires.marx;§oAppendix A§r;6>). When the generator is fully charged the voltage of each capacitor is roughly equal to the charging voltage. The total energy is the sum of the energy stored in the individual capacitors and is split equally between the ores to be processed.<br>§lControl signals§r<br>Voltages are represented by 2 signals: The first signal is simply proportional to the voltage to represent. The second signal is proportional to the voltage in the "gap" between 2 values of the first signal, thus allowing more precise control/measurements. Panel components capable of interacting with analog signals usually support this dual-channel setup. The charging voltage is controlled by the white and yellow signals. The voltages of the top and bottom capacitor are output to the magenta and pink resp. the orange and lime signals. The light blue signal is a firing control. If it is high the generator will attempt to fire. If the voltage of the bottom capacitor is lower than 125 kV or the total voltage is lower than 30%% of the maximum output voltage the generator will misfire, discharging the capacitors without actually producing lightning.<br>§lSafety§r<br>Due to the high voltages and energies involved in firing a Marx generator a safe distance should be maintained to avoid injury or death. Even outside of this area hearing protection (As provided by Immersive Engineering) is obligatory. Formulas to calculate the safe distances can be found in <link;industrialwires.marx;§oAppendix A§r;6>.<np>§lAppendix A: Formulas§r<br>Energy stored in a capacitor:<br>E=0.5*C*U^2<br>E: Energy, C: Capacitance, U: Voltage<br><br>Voltage from redstone signals:<br>U=250/255*(16*a+b)<br>U: Voltage (kV), a: First signal, b: Second signal<br><br>Safe distance (Physical damage):<br>r=sqrt(e/50,000)<br>r: Safe distance, e: Energy stored<br><br>Safe distance (Ear damage):<br>r=sqrt(e)/50<br>r: Safe distance, e: Energy stored<np>§lAppendix B: Ore Energy Values§r<br>
ie.manual.entry.industrialwires.mech_mb.name=Mechanical Multiblocks
ie.manual.entry.industrialwires.mech_mb.subtext=
ie.manual.entry.industrialwires.mech_mb=Mechanical multiblocks serve two main purposes: storing large amounts of energy and converting DC (tradename EU) to FE (tradename Flux and many others) and vice versa.<br>The multiblock is formed by placing the desired parts (see [](TODO link to parts entry)) in a row and adding two heavy engineering blocks stacked on top of each other on either side. <&0>The above plan shows a schematic for an energy storage setup with a single lead flywheel. The multiblock is formed by hitting one of the upper heavy engineering blocks with an Engineer's hammer.<br>§lMechanical§r: Some components have a maximum speed (specified in radians per second). If this speed is exceeded or the multiblock is broken while turning at more than 10%% of the maximum speed (5%% for the part being broken) the component will fail violently, usually damaging nearby objects in the process. The [speedometer](TODO) can be used to check the speed of a mechanical multiblock. Due to slight friction the multiblock will slow down over time, by 5%% per hour.<br>§lElectrical§r: Both AC and DC come in two major variants: Single-phase and Four-phase. Four phase electricity is required for most high-power parts. AC power has an additional property: synchronous or asynchronous (relative to the speed of the multiblock). This is only relevant for [rectifying/commutating parts](TODO): Power added from outside sources is asynchronous, AC power produced in the multiblock (by coils or by commutation of DC) is synchronous. Only synchronous AC can be rectified (converted to DC) using mechanical multiblocks, attempting to rectify asynchronous AC will produce an unusable waveform. If the speed of the multiblock is within 10%% of 20 radians/second both types of AC are equal: At this speed mechanical rectification work on asynchronous AC power.<br>§lEnergy storage§r: As mentioned earlier one of the main uses of mechanical multiblocks is as energy storage. The stored energy (in joules, one joule is <config;I;iwfluxPerJoule> Flux) can be calculated as 0.5*I*w*w where w is the speed of the multiblock (in radians/second) and I is the moment of inertia. The former can be obtained using a [speedometer](TODO), the latter is the sum of the inertia of each part of the multiblock (which can be found in the entry on [mechanical multiblock parts](TODO)).
ie.manual.entry.industrialwires.intro.name=Introduction
ie.manual.entry.industrialwires.intro.subtext=