Add Botania Multiblocks to StructureLib (#93)

This commit is contained in:
sisyphussy authored and GitHub committed 2025-09-03 15:09:02 +02:00
1 parent f4f7485a12
commit 825758d639
8 files changed
+381 -11

No files matched your search

+2
View File
@@ -12,5 +12,7 @@ dependencies {
compileOnly('curse.maven:cofh-lib-220333:2388748')
compileOnly('curse.maven:minefactory-reloaded-66672:2366150')
compileOnly('thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev')
compileOnly("com.github.GTNewHorizons:StructureLib:1.4.18:dev")
}
@@ -15,10 +15,15 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import cpw.mods.fml.common.FMLLog;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChunkCoordinates;
import org.apache.logging.log4j.Level;
import vazkii.botania.api.lexicon.multiblock.compat.MultiblockCompatRegistry;
import vazkii.botania.api.lexicon.multiblock.component.MultiblockComponent;
import vazkii.botania.common.Botania;
/**
* This class describes a Mutiblock object. It's used to display a
@@ -39,10 +44,14 @@ public class Multiblock {
* coords should be pivoted to the center of the structure.
*/
public void addComponent(MultiblockComponent component) {
if(getComponentForLocation(component.relPos.posX, component.relPos.posY, component.relPos.posZ) != null)
throw new IllegalArgumentException("Location in multiblock already occupied");
ChunkCoordinates pos = component.relPos;
if(getComponentForLocation(pos.posX, pos.posY, pos.posZ) != null) {
MultiblockComponent comp = getComponentForLocation(pos.posX, pos.posY, pos.posZ);
throw new IllegalArgumentException(
"Location in multiblock {x=" + pos.posX + " y=" + pos.posY + " z=" + pos.posZ + "} already occupied by block " + comp.getBlock().getLocalizedName());
}
components.add(component);
changeAxisForNewComponent(component.relPos.posX, component.relPos.posY, component.relPos.posZ);
changeAxisForNewComponent(pos.posX, pos.posY, pos.posZ);
calculateCostForNewComponent(component);
addComponentToLocationCache(component);
}
@@ -138,6 +147,28 @@ public class Multiblock {
return blocks;
}
public <T extends TileEntity> MultiblockSet makeSetRegisterStructure(
Class<T> controllerTileClass, Block controllerBlock, MultiblockComponent... extra
) {
registerStructure(controllerTileClass, controllerBlock, extra);
return makeSet();
}
public <T extends TileEntity> void registerStructure(
Class<T> controllerTileClass, Block controllerBlock, MultiblockComponent... extra
) {
if (Botania.structureLibLoaded) {
try {
MultiblockCompatRegistry.registerMultiblock(
this, controllerTileClass, controllerBlock, extra
);
} catch (Exception e) {
e.printStackTrace();
FMLLog.log(Level.ERROR, "Failed to load a Botania Multiblock into StructureLib.");
}
}
}
/**
* Makes a MultiblockSet from this Multiblock and its rotations using
* createRotations().
@@ -176,7 +207,7 @@ public class Multiblock {
pos.posX,
pos.posY,
pos.posZ
), comp);
), comp);
}
/**
@@ -0,0 +1,228 @@
package vazkii.botania.api.lexicon.multiblock.compat;
import com.gtnewhorizon.structurelib.alignment.constructable.IMultiblockInfoContainer;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
import com.gtnewhorizon.structurelib.structure.StructureDefinition;
import com.gtnewhorizon.structurelib.structure.StructureUtility;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChunkCoordinates;
import vazkii.botania.api.lexicon.multiblock.Multiblock;
import vazkii.botania.api.lexicon.multiblock.component.MultiblockComponent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Only gets loaded if StructureLib is present inside the Modpack
*/
public class MultiblockCompatRegistry {
public static <T extends TileEntity> void registerMultiblock(
Multiblock mb, Class<T> controllerTileClass,
Block controllerBlock,
MultiblockComponent... extra
) {
List<MultiblockComponent> components = mb.getComponents();
if (extra.length != 0) {
components = new ArrayList<>(components);
Collections.addAll(components, extra);
}
Map<Integer, RawStructureData> structureDataMap = new HashMap<>();
int min_x = Integer.MAX_VALUE, max_x = Integer.MIN_VALUE;
int min_y = Integer.MAX_VALUE, max_y = Integer.MIN_VALUE;
int min_z = Integer.MAX_VALUE, max_z = Integer.MIN_VALUE;
char ch = 'a';
for (MultiblockComponent component : components) {
int hash = getHash(component.getBlock(), component.getMeta());
ChunkCoordinates pos = component.getRelativePosition();
RawStructureData structureData = structureDataMap.get(hash);
if (structureData == null) {
List<ChunkCoordinates> coordinates = new ArrayList<>();
structureData = new RawStructureData(
coordinates,
component.getBlock(),
component.getMeta(),
ch++
);
structureDataMap.put(hash, structureData);
}
structureData.getPositions().add(pos);
min_x = Math.min(min_x, pos.posX); max_x = Math.max(max_x, pos.posX);
min_y = Math.min(min_y, pos.posY); max_y = Math.max(max_y, pos.posY);
min_z = Math.min(min_z, pos.posZ); max_z = Math.max(max_z, pos.posZ);
}
int x_size = max_x - min_x + 1;
int y_size = max_y - min_y + 1;
int z_size = max_z - min_z + 1;
//Data is saved in [z][y][x] format
String[][] stringData = new String[z_size][y_size];
char[] buffer = new char[x_size];
Arrays.fill(buffer, ' ');
//Since Strings and their content are immutable, this is fine.
String filledWithSpaces = new String(buffer);
for (int z = 0; z < z_size; z++) {
for (int y = 0; y < y_size; y++) {
stringData[z][y] = filledWithSpaces;
}
}
int controller_x = 0, controller_y = 0, controller_z = 0;
StringBuilder builder = new StringBuilder(x_size);
for (RawStructureData data : structureDataMap.values()) {
for (ChunkCoordinates pos : data.getPositions()) {
int x = pos.posX - min_x;
int z = pos.posZ - min_z;
//Data has to get flipped
int y = (stringData[z].length - 1) - (pos.posY - min_y);
if (data.getBlock() == controllerBlock) {
controller_x = x;
controller_y = y;
controller_z = z;
}
String s = stringData[z][y];
builder.append(s);
builder.setCharAt(x, data.getBlockIdentifier());
stringData[z][y] = builder.toString();
builder.setLength(0);
}
}
StructureDefinition.Builder<? super T> structureBuilder = IStructureDefinition.builder()
.addShape("main", stringData);
for (RawStructureData data : structureDataMap.values()) {
structureBuilder.addElement(
data.getBlockIdentifier(),
StructureUtility.ofBlock(
data.getBlock(),
data.getMetadata()
)
);
}
IMultiblockInfoContainer.registerTileClass(
controllerTileClass,
new BotaniaMultiblockInfoContainer<>(
structureBuilder.build(),
controller_x,
controller_y,
controller_z
)
);
}
private static int getHash(Block block, int metadata) {
return block.hashCode() * 31 + metadata;
}
private static class BotaniaMultiblockInfoContainer<T extends TileEntity> implements IMultiblockInfoContainer<T> {
private final IStructureDefinition<? super T> structure;
private final int x_offset, y_offset, z_offset;
public BotaniaMultiblockInfoContainer(
IStructureDefinition<? super T> structure,
int x_offset, int y_offset, int z_offset
) {
this.structure = structure;
this.x_offset = x_offset;
this.y_offset = y_offset;
this.z_offset = z_offset;
}
@Override
public void construct(ItemStack stackSize, boolean hintsOnly, T tileEntity, ExtendedFacing aSide) {
structure.buildOrHints(
tileEntity,
stackSize,
"main",
tileEntity.getWorldObj(),
noSideWay(aSide),
tileEntity.xCoord,
tileEntity.yCoord,
tileEntity.zCoord,
x_offset,
y_offset,
z_offset,
hintsOnly
);
}
@Override
public int survivalConstruct(ItemStack stackSize, int elementBudge, ISurvivalBuildEnvironment env, T tileEntity, ExtendedFacing aSide) {
return structure.survivalBuild(
tileEntity,
stackSize,
"main",
tileEntity.getWorldObj(),
noSideWay(aSide),
tileEntity.xCoord,
tileEntity.yCoord,
tileEntity.zCoord,
x_offset,
y_offset,
z_offset,
elementBudge,
env,
false
);
}
@Override
public String[] getDescription(ItemStack stackSize) {
return new String[0];
}
private static ExtendedFacing noSideWay(ExtendedFacing aSide) {
return aSide.getDirection().offsetY != 0 ? ExtendedFacing.DEFAULT : aSide.getOppositeDirection();
}
}
private static class RawStructureData {
private final List<ChunkCoordinates> positions;
private final char identifier;
private final Block block;
private final int metadata;
public RawStructureData(List<ChunkCoordinates> positions, Block block, int metadata, char identifier) {
this.positions = positions;
this.identifier = identifier;
this.block = block;
this.metadata = metadata;
}
public Block getBlock() {
return block;
}
public int getMetadata() {
return metadata;
}
public List<ChunkCoordinates> getPositions() {
return positions;
}
public char getBlockIdentifier() {
return identifier;
}
}
}
@@ -42,6 +42,7 @@ public class Botania {
public static boolean coloredLightsLoaded = false;
public static boolean etFuturumLoaded = false;
public static boolean storageDrawersLoaded = false;
public static boolean structureLibLoaded = false;
public static ILightHelper lightHelper;
@@ -61,6 +62,7 @@ public class Botania {
coloredLightsLoaded = Loader.isModLoaded("easycoloredlights");
etFuturumLoaded = Loader.isModLoaded("etfuturum");
storageDrawersLoaded = Loader.isModLoaded("StorageDrawers");
structureLibLoaded = Loader.isModLoaded("structurelib");
lightHelper = coloredLightsLoaded ? new LightHelperColored() : new LightHelperVanilla();
proxy.preInit(event);
@@ -116,7 +116,7 @@ public class TileAlfPortal extends TileMod {
mb.addComponent(0, 1, 0, ModBlocks.alfPortal, 0);
mb.setRenderOffset(0, -1, 0);
return mb.makeSet();
return mb.makeSetRegisterStructure(TileAlfPortal.class, ModBlocks.alfPortal);
}
@Override
@@ -13,8 +13,13 @@ package vazkii.botania.common.block.tile;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
@@ -22,25 +27,30 @@ import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.internal.VanillaPacketDispatcher;
import vazkii.botania.api.lexicon.multiblock.Multiblock;
import vazkii.botania.api.lexicon.multiblock.MultiblockSet;
import vazkii.botania.api.lexicon.multiblock.component.FlowerComponent;
import vazkii.botania.api.lexicon.multiblock.component.MultiblockComponent;
import vazkii.botania.api.mana.IManaPool;
import vazkii.botania.api.mana.spark.ISparkAttachable;
import vazkii.botania.api.mana.spark.ISparkEntity;
import vazkii.botania.api.mana.spark.SparkHelper;
import vazkii.botania.client.core.helper.RenderHelper;
import vazkii.botania.common.Botania;
import vazkii.botania.common.block.BlockModFlower;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.item.block.ItemBlockMod;
public class TileEnchanter extends TileMod implements ISparkAttachable {
@@ -81,20 +91,68 @@ public class TileEnchanter extends TileMod implements ISparkAttachable {
{ -1, 0, -1 }, { 1, 0, -1 }, { -1, 0, 1 }, { 1, 0, 1 }
};
//Note: It currently doesn't show up when looking at a Lapis Block in NEI and I have no idea how to fix it.
public static MultiblockSet makeMultiblockSet() {
Multiblock mb = new Multiblock();
for(int[] o : OBSIDIAN_LOCATIONS)
mb.addComponent(o[0], o[1] + 1, o[2], Blocks.obsidian, 0);
//Flowers don't seem to properly work in StructureLib (since they need a block below them)
//This solution also allows the flowers to cycle through each other
//Not sure if there's a better solution for this, but hey it works
Block physicsDefyingFlower = new StructureLibFlower();
List<MultiblockComponent> extraDisplayList = new ArrayList<>();
for(int[] p : PYLON_LOCATIONS[0]) {
mb.addComponent(p[0], p[1] + 1, p[2], ModBlocks.pylon, 0);
mb.addComponent(new FlowerComponent(new ChunkCoordinates(p[0], p[1], p[2]), ModBlocks.flower));
extraDisplayList.add(new MultiblockComponent(
new ChunkCoordinates(p[0], p[1], p[2]),
physicsDefyingFlower,
0
));
extraDisplayList.add(new MultiblockComponent(
new ChunkCoordinates(p[0], p[1] - 1, p[2]),
Blocks.grass,
0
));
}
for(int[] f : FLOWER_LOCATIONS) {
extraDisplayList.add(new MultiblockComponent(
new ChunkCoordinates(f[0], f[1] + 1, f[2]),
physicsDefyingFlower,
0
));
extraDisplayList.add(new MultiblockComponent(
new ChunkCoordinates(f[0], f[1], f[2]),
Blocks.grass,
0
));
}
for(int[] f : FLOWER_LOCATIONS)
mb.addComponent(new FlowerComponent(new ChunkCoordinates(f[0], f[1] + 1, f[2]), ModBlocks.flower));
mb.addComponent(0, 1, 0, Blocks.lapis_block, 0);
mb.addComponent(0, 1, 0, ModBlocks.enchanter, 0);
mb.registerStructure(
TileEnchanter.class,
ModBlocks.enchanter,
extraDisplayList.toArray(new MultiblockComponent[0])
);
//Add the actual flowers
for(int[] p : PYLON_LOCATIONS[0]) {
mb.addComponent(new FlowerComponent(
new ChunkCoordinates(p[0], p[1], p[2]),
ModBlocks.flower
));
}
for(int[] f : FLOWER_LOCATIONS) {
mb.addComponent(new FlowerComponent(
new ChunkCoordinates(f[0], f[1] + 1, f[2]),
ModBlocks.flower
));
}
return mb.makeSet();
}
@@ -128,7 +186,7 @@ public class TileEnchanter extends TileMod implements ISparkAttachable {
if(getBlockMetadata() < PYLON_LOCATIONS.length)
for(int[] pylon : PYLON_LOCATIONS[getBlockMetadata()]) {
TileEntity tile = worldObj.getTileEntity(xCoord + pylon[0], yCoord + pylon[1], zCoord + pylon[2]);
if(tile != null && tile instanceof TilePylon)
if(tile instanceof TilePylon)
((TilePylon) tile).activated = false;
}
@@ -429,4 +487,52 @@ public class TileEnchanter extends TileMod implements ISparkAttachable {
}
}
private static class StructureLibFlower extends BlockModFlower {
public StructureLibFlower() {
super();
}
@Override
public Block setBlockName(String par1Str) {
GameRegistry.registerBlock(this, ItemBlockMod.class, "flower_structurelib");
return this;
}
@Override
public IIcon getIcon(int par1, int par2) {
return super.getIcon(par1, (int) (BotaniaAPI.internalHandler.getWorldElapsedTicks() / 20) % 16);
}
@Override
public String getUnlocalizedName() {
return "tile.flower_structurelib";
}
@Override
public void registerBlockIcons(IIconRegister par1IconRegister) {
//has to be empty
}
@Override
public void getSubBlocks(Item par1, CreativeTabs par2CreativeTabs, List par3List) {
//has to be empty
}
@Override
public boolean canPlaceBlockAt(World worldIn, int x, int y, int z) {
return true;
}
@Override
public boolean canBlockStay(World worldIn, int x, int y, int z) {
return true;
}
@Override
protected boolean canPlaceBlockOn(Block ground) {
return true;
}
}
}
@@ -57,7 +57,7 @@ public class TileTerraPlate extends TileMod implements ISparkAttachable {
mb.addComponent(0, 1, 0, ModBlocks.terraPlate, 0);
mb.setRenderOffset(0, 1, 0);
return mb.makeSet();
return mb.makeSetRegisterStructure(TileTerraPlate.class, ModBlocks.terraPlate);
}
@Override
@@ -650,6 +650,7 @@ tile.botania:flower12.name=Mystical Brown Flower
tile.botania:flower13.name=Mystical Green Flower
tile.botania:flower14.name=Mystical Red Flower
tile.botania:flower15.name=Mystical Black Flower
tile.botania:flower_structurelib.name=Any Botania Flower
tile.botania:altar0.name=Petal Apothecary
tile.botania:altar1.name=Forest Petal Apothecary
tile.botania:altar2.name=Plains Petal Apothecary