George Paton 84601f685f NBTStructureLib features brought back to NTM, including:
* `/locate` command
* custom spawning rules (eg. make something always spawn at 0,0)
* code structure reorg
* extra logging for misconfigured structures
2025-08-08 14:51:45 +10:00

363 lines
12 KiB
Java

package com.hbm.blocks.generic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import com.hbm.blocks.IBlockSideRotation;
import com.hbm.blocks.ILookOverlay;
import com.hbm.blocks.ITooltipProvider;
import com.hbm.blocks.ModBlocks;
import com.hbm.config.StructureConfig;
import com.hbm.itempool.ItemPool;
import com.hbm.lib.RefStrings;
import com.hbm.main.MainRegistry;
import com.hbm.tileentity.TileEntityLoadedBase;
import com.hbm.util.BufferUtil;
import com.hbm.util.LootGenerator;
import com.hbm.util.i18n.I18nUtil;
import com.hbm.world.gen.nbt.INBTTileEntityTransformable;
import com.mojang.authlib.GameProfile;
import api.hbm.block.IToolable;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.client.event.RenderGameOverlayEvent.Pre;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.FakePlayerFactory;
public class BlockWandLoot extends BlockContainer implements ILookOverlay, IToolable, ITooltipProvider, IBlockSideRotation {
@SideOnly(Side.CLIENT) protected IIcon iconTop;
public BlockWandLoot() {
super(Material.iron);
}
@Override
@SideOnly(Side.CLIENT)
public void registerBlockIcons(IIconRegister iconRegister) {
this.blockIcon = iconRegister.registerIcon(RefStrings.MODID + ":wand_loot");
this.iconTop = iconRegister.registerIcon(RefStrings.MODID + ":wand_loot_top");
}
@Override
public IIcon getIcon(int side, int meta) {
return (side <= 1) ? iconTop : blockIcon;
}
@Override
public int getRotationFromSide(IBlockAccess world, int x, int y, int z, int side) {
if(side == 0) return IBlockSideRotation.topToBottom(world.getBlockMetadata(x, y, z));
if(side == 1) return world.getBlockMetadata(x, y, z);
return 0;
}
@Override
public int getRenderType() {
return IBlockSideRotation.getRenderType();
}
@Override
public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase player, ItemStack itemStack) {
int i = MathHelper.floor_double(player.rotationYaw * 4.0F / 360.0F + 0.5D) & 3;
if(i == 0) world.setBlockMetadataWithNotify(x, y, z, 3, 2);
if(i == 1) world.setBlockMetadataWithNotify(x, y, z, 2, 2);
if(i == 2) world.setBlockMetadataWithNotify(x, y, z, 0, 2);
if(i == 3) world.setBlockMetadataWithNotify(x, y, z, 1, 2);
TileEntity te = world.getTileEntity(x, y, z);
if(!(te instanceof TileEntityWandLoot)) return;
((TileEntityWandLoot) te).placedRotation = player.rotationYaw;
}
@Override
public void printHook(Pre event, World world, int x, int y, int z) {
TileEntity te = world.getTileEntity(x, y, z);
if(!(te instanceof TileEntityWandLoot)) return;
TileEntityWandLoot loot = (TileEntityWandLoot) te;
List<String> text = new ArrayList<String>();
text.add("Will replace with: " + loot.replaceBlock.getUnlocalizedName());
text.add(" meta: " + loot.replaceMeta);
text.add("Loot pool: " + loot.poolName);
if(loot.replaceBlock != ModBlocks.deco_loot) {
text.add("Minimum items: " + loot.minItems);
text.add("Maximum items: " + loot.maxItems);
}
ILookOverlay.printGeneric(event, I18nUtil.resolveKey(getUnlocalizedName() + ".name"), 0xffff00, 0x404000, text);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void addInformation(ItemStack stack, EntityPlayer player, List list, boolean ext) {
list.add("Define loot crates/piles in .nbt structures");
list.add(EnumChatFormatting.GOLD + "Use screwdriver to increase/decrease minimum loot");
list.add(EnumChatFormatting.GOLD + "Use hand drill to increase/decrease maximum loot");
list.add(EnumChatFormatting.GOLD + "Use defuser to cycle loot types");
list.add(EnumChatFormatting.GOLD + "Use container block to set the block that spawns with loot inside");
}
@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) {
TileEntity te = world.getTileEntity(x, y, z);
if(!(te instanceof TileEntityWandLoot)) return false;
TileEntityWandLoot loot = (TileEntityWandLoot) te;
if(!player.isSneaking()) {
Block block = getLootableBlock(world, player.getHeldItem());
if(block != null) {
loot.replaceBlock = block;
loot.replaceMeta = player.getHeldItem().getItemDamage();
List<String> poolNames = loot.getPoolNames(block == ModBlocks.deco_loot);
if(!poolNames.contains(loot.poolName)) {
loot.poolName = poolNames.get(0);
}
return true;
}
}
return false;
}
private Block getLootableBlock(World world, ItemStack stack) {
if(stack == null) return null;
if(stack.getItem() instanceof ItemBlock) {
Block block = ((ItemBlock) stack.getItem()).field_150939_a;
if(block == ModBlocks.deco_loot) return block;
if(block instanceof ITileEntityProvider) {
TileEntity te = ((ITileEntityProvider) block).createNewTileEntity(world, 12);
if(te instanceof IInventory) return block;
}
}
return null;
}
@Override
public boolean onScrew(World world, EntityPlayer player, int x, int y, int z, int side, float fX, float fY, float fZ, ToolType tool) {
TileEntity te = world.getTileEntity(x, y, z);
if(!(te instanceof TileEntityWandLoot)) return false;
TileEntityWandLoot loot = (TileEntityWandLoot) te;
switch(tool) {
case SCREWDRIVER:
if(player.isSneaking()) {
loot.minItems--;
if(loot.minItems < 0) loot.minItems = 0;
} else {
loot.minItems++;
loot.maxItems = Math.max(loot.minItems, loot.maxItems);
}
return true;
case HAND_DRILL:
if(player.isSneaking()) {
loot.maxItems--;
if(loot.maxItems < 0) loot.maxItems = 0;
loot.minItems = Math.min(loot.minItems, loot.maxItems);
} else {
loot.maxItems++;
}
return true;
case DEFUSER:
List<String> poolNames = loot.getPoolNames(loot.replaceBlock == ModBlocks.deco_loot);
int index = poolNames.indexOf(loot.poolName);
index += player.isSneaking() ? -1 : 1;
index = MathHelper.clamp_int(index, 0, poolNames.size() - 1);
loot.poolName = poolNames.get(index);
return true;
default: return false;
}
}
@Override
public TileEntity createNewTileEntity(World world, int meta) {
return new TileEntityWandLoot();
}
public static class TileEntityWandLoot extends TileEntityLoadedBase implements INBTTileEntityTransformable {
private boolean triggerReplace;
private Block replaceBlock = ModBlocks.deco_loot;
private int replaceMeta;
private String poolName = LootGenerator.LOOT_BOOKLET;
private int minItems;
private int maxItems = 1;
private float placedRotation;
private static final GameProfile FAKE_PROFILE = new GameProfile(UUID.fromString("839eb18c-50bc-400c-8291-9383f09763e7"), "[NTM]");
private static FakePlayer fakePlayer;
@Override
public void updateEntity() {
if(!worldObj.isRemote) {
if(triggerReplace) {
// On the first tick of this TE, replace with intended block and fill with loot
replace();
} else {
networkPackNT(15);
}
}
}
private void replace() {
if(!(worldObj.getBlock(xCoord, yCoord, zCoord) instanceof BlockWandLoot)) {
MainRegistry.logger.warn("Somehow the block at: " + xCoord + ", " + yCoord + ", " + zCoord + " isn't a loot block but we're doing a TE update as if it is, cancelling!");
return;
}
WeightedRandomChestContent[] pool = ItemPool.getPool(poolName);
worldObj.setBlock(xCoord, yCoord, zCoord, replaceBlock, replaceMeta, 2);
TileEntity te = worldObj.getTileEntity(xCoord, yCoord, zCoord);
if(te == null || te instanceof TileEntityWandLoot) {
// Some generator has broken the TE->block relationship, which, honestly, rude.
// so we're just gonna hop in and force update the TE
MainRegistry.logger.warn("TE set incorrectly at: " + xCoord + ", " + yCoord + ", " + zCoord + ". If you're using some sort of world generation mod, report it to the author!");
te = replaceBlock.createTileEntity(worldObj, replaceMeta);
worldObj.setTileEntity(xCoord, yCoord, zCoord, te);
}
if(te instanceof IInventory) {
int count = minItems;
if(maxItems - minItems > 0) count += worldObj.rand.nextInt(maxItems - minItems);
WeightedRandomChestContent.generateChestContents(worldObj.rand, pool, (IInventory) te, count);
} else if(te instanceof BlockLoot.TileEntityLoot) {
LootGenerator.applyLoot(worldObj, xCoord, yCoord, zCoord, poolName);
}
// Shouldn't happen but let's guard anyway, if it fails we just don't rotate the chest block correctly
if(!(worldObj instanceof WorldServer)) return;
try {
if(fakePlayer == null || fakePlayer.worldObj != worldObj) {
fakePlayer = FakePlayerFactory.get((WorldServer)worldObj, FAKE_PROFILE);
}
fakePlayer.rotationYaw = fakePlayer.rotationYawHead = placedRotation;
ItemStack fakeStack = new ItemStack(replaceBlock, 1, replaceMeta);
replaceBlock.onBlockPlacedBy(worldObj, xCoord, yCoord, zCoord, fakePlayer, fakeStack);
} catch(Exception ex) {
MainRegistry.logger.warn("Failed to correctly rotate loot block at: " + xCoord + ", " + yCoord + ", " + zCoord);
MainRegistry.logger.catching(ex);
}
}
private List<String> getPoolNames(boolean loot) {
if(loot) return Arrays.asList(LootGenerator.getLootNames());
List<String> names = new ArrayList<>();
names.addAll(ItemPool.pools.keySet());
return names;
}
@Override
public void transformTE(World world, int coordBaseMode) {
triggerReplace = !StructureConfig.debugStructures;
placedRotation = MathHelper.wrapAngleTo180_float(placedRotation + coordBaseMode * 90);
}
@Override
public void writeToNBT(NBTTagCompound nbt) {
super.writeToNBT(nbt);
Block writeBlock = replaceBlock == null ? ModBlocks.deco_loot : replaceBlock;
nbt.setString("block", GameRegistry.findUniqueIdentifierFor(writeBlock).toString());
nbt.setInteger("meta", replaceMeta);
nbt.setInteger("min", minItems);
nbt.setInteger("max", maxItems);
nbt.setString("pool", poolName);
nbt.setFloat("rot", placedRotation);
nbt.setBoolean("trigger", triggerReplace);
}
@Override
public void readFromNBT(NBTTagCompound nbt) {
super.readFromNBT(nbt);
replaceBlock = Block.getBlockFromName(nbt.getString("block"));
replaceMeta = nbt.getInteger("meta");
minItems = nbt.getInteger("min");
maxItems = nbt.getInteger("max");
poolName = nbt.getString("pool");
placedRotation = nbt.getFloat("rot");
if(replaceBlock == null) replaceBlock = ModBlocks.deco_loot;
triggerReplace = nbt.getBoolean("trigger");
}
@Override
public void serialize(ByteBuf buf) {
buf.writeInt(Block.getIdFromBlock(replaceBlock));
buf.writeInt(replaceMeta);
buf.writeInt(minItems);
buf.writeInt(maxItems);
BufferUtil.writeString(buf, poolName);
}
@Override
public void deserialize(ByteBuf buf) {
replaceBlock = Block.getBlockById(buf.readInt());
replaceMeta = buf.readInt();
minItems = buf.readInt();
maxItems = buf.readInt();
poolName = BufferUtil.readString(buf);
}
}
}