Position this mod as a port of BCx from the Minecraft 1.2.5 era. - Rename the mod to BCx-modern (modid bcxmodern) and the package to net.kanawo.bcxmodern; the main class is now BCxModern and takes its version from Tags.VERSION. - Move assets to assets/bcxmodern and rename the lang key and EMC save data to match. - Fill in mcmod.info (description, author agomizi1cm, credits, dependencies) and the LICENSE copyright holder. - Add a README. - Remove template leftovers: CODEOWNERS, LICENSE-template, the greeting Config, template comments, empty event handlers and the commented-out NEI dependency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
295 lines
8.6 KiB
Java
295 lines
8.6 KiB
Java
package net.kanawo.bcxmodern.tile;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
import net.minecraft.entity.EntityLivingBase;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
|
|
import buildcraft.builders.TileBuilder;
|
|
import buildcraft.core.blueprints.BptBuilderBase;
|
|
import buildcraft.core.blueprints.BptBuilderBlueprint;
|
|
import buildcraft.core.blueprints.RequirementItemStack;
|
|
import io.netty.buffer.ByteBuf;
|
|
import moze_intel.projecte.utils.EMCHelper;
|
|
|
|
/**
|
|
* A Builder that pays for building materials with EMC.
|
|
* <p>
|
|
* The EMC is not stored in the tile but in its owner's balance in {@link EmcPool}, so all EMC Builders a player owns
|
|
* in a world share it. The owner is the player who placed the builder, or for one built from a blueprint, the first
|
|
* player who opens it.
|
|
* <p>
|
|
* BuildCraft only builds blocks whose materials are present in the builder's inventory, and consumes them by
|
|
* shrinking the stacks it finds there. While {@link #build()} runs, this tile therefore appends virtual slots after
|
|
* the real inventory, one per needed item that has an EMC value, sized to what the stored EMC can afford. After the
|
|
* build step the amount taken from each virtual slot is charged at 1 EMC per EMC value. Real items in the material
|
|
* slots come first in the inventory, so they are used before EMC.
|
|
* <p>
|
|
* Outside {@link #build()} the virtual slots are hidden, so GUIs, hoppers, pipes and robots only ever see the real
|
|
* inventory.
|
|
*/
|
|
public class TileEMCBuilder extends TileBuilder {
|
|
|
|
/** NBT keys of the owner's UUID; SchematicEMCBuilder removes them from blueprints. */
|
|
public static final String OWNER_MOST = "OwnerUUIDMost";
|
|
public static final String OWNER_LEAST = "OwnerUUIDLeast";
|
|
|
|
private static final int EMC_SYNC_INTERVAL = 10;
|
|
|
|
private UUID owner;
|
|
|
|
/** Owner's balance as last received from the server. Client side only. */
|
|
private double clientEmc;
|
|
/** Owner's balance as last sent to clients. Server side only. */
|
|
private double sentEmc = -1;
|
|
private int ticksSinceEmcSync;
|
|
|
|
private final List<EmcSlot> emcSlots = new ArrayList<>();
|
|
private boolean emcSlotsExposed;
|
|
|
|
/** A virtual material slot backed by stored EMC. */
|
|
private static final class EmcSlot {
|
|
|
|
final long emcPerItem;
|
|
final int initialSize;
|
|
ItemStack stack;
|
|
|
|
EmcSlot(ItemStack stack, long emcPerItem) {
|
|
this.stack = stack;
|
|
this.emcPerItem = emcPerItem;
|
|
this.initialSize = stack.stackSize;
|
|
}
|
|
|
|
int consumed() {
|
|
return stack == null ? initialSize : initialSize - stack.stackSize;
|
|
}
|
|
}
|
|
|
|
/** The owner's EMC balance; on the client, as last synced. */
|
|
public double getStoredEmc() {
|
|
if (worldObj.isRemote) {
|
|
return clientEmc;
|
|
}
|
|
|
|
return owner == null ? 0
|
|
: EmcPool.get(worldObj)
|
|
.get(owner);
|
|
}
|
|
|
|
/** Makes the player the owner if the builder has none yet. Server side only. */
|
|
public void claimOwner(EntityPlayer player) {
|
|
if (owner == null) {
|
|
owner = player.getUniqueID();
|
|
markDirty();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onBlockPlacedBy(EntityLivingBase entity, ItemStack stack) {
|
|
super.onBlockPlacedBy(entity, stack);
|
|
|
|
if (entity instanceof EntityPlayer) {
|
|
owner = entity.getUniqueID();
|
|
}
|
|
}
|
|
|
|
/** Converts the whole stack into EMC for the owner. Server side only. */
|
|
public void consumeForEmc(ItemStack stack) {
|
|
long value = EMCHelper.getEmcValue(stack);
|
|
|
|
if (owner == null || value <= 0 || stack.stackSize <= 0) {
|
|
return;
|
|
}
|
|
|
|
EmcPool.get(worldObj)
|
|
.add(owner, (double) value * stack.stackSize);
|
|
stack.stackSize = 0;
|
|
sendNetworkUpdate();
|
|
}
|
|
|
|
@Override
|
|
public void updateEntity() {
|
|
super.updateEntity();
|
|
|
|
if (worldObj.isRemote) {
|
|
return;
|
|
}
|
|
|
|
// The balance can also change through the owner's other EMC Builders, so poll it.
|
|
if (++ticksSinceEmcSync >= EMC_SYNC_INTERVAL) {
|
|
ticksSinceEmcSync = 0;
|
|
|
|
if (getStoredEmc() != sentEmc) {
|
|
sendNetworkUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void build() {
|
|
BptBuilderBase bpt = getBlueprint();
|
|
double emc = getStoredEmc();
|
|
|
|
if (!(bpt instanceof BptBuilderBlueprint) || emc <= 0) {
|
|
super.build();
|
|
return;
|
|
}
|
|
|
|
// The needed item list is only filled once the builder has been initialized.
|
|
bpt.initialize();
|
|
prepareEmcSlots(((BptBuilderBlueprint) bpt).getNeededItems(), emc);
|
|
|
|
emcSlotsExposed = true;
|
|
|
|
try {
|
|
super.build();
|
|
} finally {
|
|
emcSlotsExposed = false;
|
|
chargeEmcSlots();
|
|
}
|
|
}
|
|
|
|
private void prepareEmcSlots(List<RequirementItemStack> needs, double emc) {
|
|
emcSlots.clear();
|
|
|
|
if (needs == null) {
|
|
return;
|
|
}
|
|
|
|
for (RequirementItemStack need : needs) {
|
|
if (need.stack == null || need.stack.getItem() == null || need.size <= 0) {
|
|
continue;
|
|
}
|
|
|
|
long value = EMCHelper.getEmcValue(need.stack);
|
|
|
|
if (value <= 0) {
|
|
continue;
|
|
}
|
|
|
|
int affordable = (int) Math.min(Integer.MAX_VALUE, Math.floor(emc / value));
|
|
int size = Math.min(Math.min(need.stack.getMaxStackSize(), need.size), affordable);
|
|
|
|
if (size <= 0) {
|
|
continue;
|
|
}
|
|
|
|
ItemStack stack = need.stack.copy();
|
|
stack.stackSize = size;
|
|
emcSlots.add(new EmcSlot(stack, value));
|
|
}
|
|
}
|
|
|
|
private void chargeEmcSlots() {
|
|
double cost = 0;
|
|
|
|
for (EmcSlot slot : emcSlots) {
|
|
cost += (double) slot.consumed() * slot.emcPerItem;
|
|
}
|
|
|
|
emcSlots.clear();
|
|
|
|
if (cost > 0) {
|
|
EmcPool.get(worldObj)
|
|
.add(owner, -cost);
|
|
}
|
|
}
|
|
|
|
private int realSize() {
|
|
return super.getSizeInventory();
|
|
}
|
|
|
|
private EmcSlot emcSlot(int i) {
|
|
return emcSlotsExposed && i >= realSize() ? emcSlots.get(i - realSize()) : null;
|
|
}
|
|
|
|
@Override
|
|
public int getSizeInventory() {
|
|
return emcSlotsExposed ? realSize() + emcSlots.size() : realSize();
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getStackInSlot(int i) {
|
|
EmcSlot slot = emcSlot(i);
|
|
return slot != null ? slot.stack : super.getStackInSlot(i);
|
|
}
|
|
|
|
@Override
|
|
public ItemStack decrStackSize(int i, int amount) {
|
|
EmcSlot slot = emcSlot(i);
|
|
|
|
if (slot == null) {
|
|
return super.decrStackSize(i, amount);
|
|
}
|
|
|
|
if (slot.stack == null) {
|
|
return null;
|
|
}
|
|
|
|
ItemStack taken = slot.stack.splitStack(Math.min(amount, slot.stack.stackSize));
|
|
|
|
if (slot.stack.stackSize <= 0) {
|
|
slot.stack = null;
|
|
}
|
|
|
|
return taken;
|
|
}
|
|
|
|
@Override
|
|
public void setInventorySlotContents(int i, ItemStack stack) {
|
|
EmcSlot slot = emcSlot(i);
|
|
|
|
if (slot == null) {
|
|
super.setInventorySlotContents(i, stack);
|
|
return;
|
|
}
|
|
|
|
// Schematic.useItem only writes to a slot once its stack is used up (possibly leaving a container item
|
|
// behind). Either way the whole virtual stack has been consumed; container items are not kept.
|
|
slot.stack = null;
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getStackInSlotOnClosing(int i) {
|
|
return emcSlot(i) != null ? null : super.getStackInSlotOnClosing(i);
|
|
}
|
|
|
|
@Override
|
|
public boolean isItemValidForSlot(int i, ItemStack stack) {
|
|
return emcSlot(i) == null && super.isItemValidForSlot(i, stack);
|
|
}
|
|
|
|
@Override
|
|
public void readFromNBT(NBTTagCompound nbt) {
|
|
super.readFromNBT(nbt);
|
|
owner = nbt.hasKey(OWNER_MOST) ? new UUID(nbt.getLong(OWNER_MOST), nbt.getLong(OWNER_LEAST)) : null;
|
|
}
|
|
|
|
@Override
|
|
public void writeToNBT(NBTTagCompound nbt) {
|
|
super.writeToNBT(nbt);
|
|
|
|
if (owner != null) {
|
|
nbt.setLong(OWNER_MOST, owner.getMostSignificantBits());
|
|
nbt.setLong(OWNER_LEAST, owner.getLeastSignificantBits());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void writeData(ByteBuf stream) {
|
|
super.writeData(stream);
|
|
sentEmc = getStoredEmc();
|
|
stream.writeDouble(sentEmc);
|
|
}
|
|
|
|
@Override
|
|
public void readData(ByteBuf stream) {
|
|
super.readData(stream);
|
|
clientEmc = stream.readDouble();
|
|
}
|
|
}
|