Compare commits

..
3 Commits
Author SHA1 Message Date
agomizi1cmandClaude Opus 5.5 28b1841c2a Rebrand as BCx-modern, a 1.7.10 port of BCx
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>
2026-10-27 10:13:59 +09:00
agomizi1cm ecde802cdb modify .gitignore 2026-10-27 01:03:25 +09:00
agomizi1cmandClaude Opus 5.5 870e5d13eb Add EMC Builder: a BuildCraft Builder that pays for materials with EMC
- TileEMCBuilder extends TileBuilder. During build() it exposes virtual
  material slots backed by EMC, then charges the EMC value (1:1) of what
  was used. Real items and tank fluids are still used first.
- EMC is stored per player per world (EmcPool, WorldSavedData), so all
  EMC Builders a player owns in a world share one balance.
- GUI based on BuildCraft's Builder GUI, with an EMC consume slot like the
  Transmutation Table's and the owner's balance shown.
- Blueprint schematic strips the owner; the first player to open an
  unowned builder becomes its owner.
- Lit top while building is not drawn.
- Shapeless recipe: Builder + Transmutation Table.
- Depend on local BuildCraft 7.1.27 and FMProjectE dev jars in libs/
  (not committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 00:59:45 +09:00
29 changed files with 1091 additions and 137 deletions

No files matched your search

+1
View File
@@ -35,3 +35,4 @@ addon.local.gradle.kts
addon.late.local.gradle addon.late.local.gradle
addon.late.local.gradle.kts addon.late.local.gradle.kts
layout.json layout.json
/libs/
-3
View File
@@ -1,3 +0,0 @@
# Any Github changes require admin approval
/.github/** @GTNewHorizons/admin
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+38
View File
@@ -0,0 +1,38 @@
# BCx-modern
Minecraft 1.2.5 時代の BuildCraft × Equivalent Exchange アドオン「BCx」を、Minecraft 1.7.10 (Forge 10.13.4.1614) 向けに移植したものです。
## 追加要素
### EMC Builder
BuildCraft の Builder を拡張し、建築に必要な資材を EMC で支払えるようにしたブロックです。
- 内部インベントリや液体タンクにある資材が先に使われ、足りない分を EMC で補います(EMC 値 1:1)。
- EMC はワールドごと・プレイヤーごとに保存され、同じプレイヤーが所有する EMC Builder はすべて同じ残高を共有します。
- GUI の EMC スロットにアイテムを入れると、Transmutation Table と同様に EMC に変換されます。
- レシピ: Builder + Transmutation Table(不定形)
## 必要な Mod
- BuildCraft 7.1.x(Builders)
- ProjectE
## ビルド
開発用の依存 jar(`libs/` に配置、リポジトリには含まない):
- `buildcraft-7.1.27-dev.jar`(BuildCraft で `gradlew deobfJar`)
- `FMProjectE-*-dev.jar`(FMProjectE で `gradlew jar`)
```bash
./gradlew build
```
## 作者
agomizi1cm
## ライセンス
MIT
+5 -1
View File
@@ -34,5 +34,9 @@
* For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph
*/ */
dependencies { dependencies {
//runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.7.69-GTNH:dev") // Local deobfuscated (MCP-named) dev jars placed in libs/:
// - BuildCraft 7.1.27 (gradlew deobfJar -> buildcraft-7.1.27-dev.jar)
// - FMProjectE (gradlew jar -> FMProjectE-*-dev.jar)
devOnlyNonPublishable(files("libs/buildcraft-7.1.27-dev.jar"))
devOnlyNonPublishable(fileTree("libs") { include("FMProjectE-*-dev.jar") })
} }
+4 -4
View File
@@ -3,15 +3,15 @@
gtnh.settings.blowdryerTag = 0.2.2 gtnh.settings.blowdryerTag = 0.2.2
# Human-readable mod name, available for mcmod.info population. # Human-readable mod name, available for mcmod.info population.
modName = EMCBuilder modName = BCx-modern
# Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation. # Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation.
# Conventionally lowercase. # Conventionally lowercase.
modId = emcbuilder modId = bcxmodern
# Root package of the mod, used to find various classes in other properties, # Root package of the mod, used to find various classes in other properties,
# mcmod.info substitution, enabling assertions in run tasks, etc. # mcmod.info substitution, enabling assertions in run tasks, etc.
modGroup = net.kanawo.emcbuilder modGroup = net.kanawo.bcxmodern
# Whether to use modGroup as the maven publishing group. # Whether to use modGroup as the maven publishing group.
# When false, com.github.GTNewHorizons is used. # When false, com.github.GTNewHorizons is used.
@@ -74,7 +74,7 @@ enableGenericInjection = true
# Generate a class with a String field for the mod version named as defined below. # Generate a class with a String field for the mod version named as defined below.
# If generateGradleTokenClass is empty or not missing, no such class will be generated. # If generateGradleTokenClass is empty or not missing, no such class will be generated.
# If gradleTokenVersion is empty or missing, the field will not be present in the class. # If gradleTokenVersion is empty or missing, the field will not be present in the class.
generateGradleTokenClass = net.kanawo.emcbuilder.Tags generateGradleTokenClass = net.kanawo.bcxmodern.Tags
# Name of the token containing the project's current version to generate/replace. # Name of the token containing the project's current version to generate/replace.
gradleTokenVersion = VERSION gradleTokenVersion = VERSION
@@ -0,0 +1,37 @@
package net.kanawo.bcxmodern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.FMLPreInitializationEvent;
@Mod(
modid = BCxModern.MODID,
version = Tags.VERSION,
name = "BCx-modern",
acceptedMinecraftVersions = "[1.7.10]",
dependencies = "required-after:BuildCraft|Builders;required-after:ProjectE")
public class BCxModern {
public static final String MODID = "bcxmodern";
public static final Logger LOG = LogManager.getLogger(MODID);
@Mod.Instance(MODID)
public static BCxModern instance;
@SidedProxy(clientSide = "net.kanawo.bcxmodern.ClientProxy", serverSide = "net.kanawo.bcxmodern.CommonProxy")
public static CommonProxy proxy;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
proxy.preInit(event);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
proxy.init(event);
}
}
@@ -0,0 +1,19 @@
package net.kanawo.bcxmodern;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import buildcraft.core.render.RenderBuilder;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.event.FMLInitializationEvent;
public class ClientProxy extends CommonProxy {
@Override
public void init(FMLInitializationEvent event) {
super.init(event);
// The Builder's own renderer (RenderBuilderTile) also draws BuildCraft's lit top while building. Its base
// renderer draws only the area box, path lasers and flying items.
ClientRegistry.bindTileEntitySpecialRenderer(TileEMCBuilder.class, new RenderBuilder());
}
}
@@ -0,0 +1,37 @@
package net.kanawo.bcxmodern;
import net.kanawo.bcxmodern.block.BlockEMCBuilder;
import net.kanawo.bcxmodern.block.SchematicEMCBuilder;
import net.kanawo.bcxmodern.gui.GuiHandler;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.item.ItemStack;
import buildcraft.BuildCraftBuilders;
import buildcraft.api.blueprints.BuilderAPI;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import moze_intel.projecte.gameObjs.ObjHandler;
public class CommonProxy {
public static BlockEMCBuilder emcBuilderBlock;
public void preInit(FMLPreInitializationEvent event) {
emcBuilderBlock = new BlockEMCBuilder();
GameRegistry.registerBlock(emcBuilderBlock, "emcBuilder");
GameRegistry.registerTileEntity(TileEMCBuilder.class, BCxModern.MODID + ":EMCBuilder");
}
public void init(FMLInitializationEvent event) {
NetworkRegistry.INSTANCE.registerGuiHandler(BCxModern.instance, new GuiHandler());
BuilderAPI.schematicRegistry.registerSchematicBlock(emcBuilderBlock, SchematicEMCBuilder.class);
GameRegistry.addShapelessRecipe(
new ItemStack(emcBuilderBlock),
BuildCraftBuilders.builderBlock,
ObjHandler.transmuteStone);
}
}
@@ -0,0 +1,96 @@
package net.kanawo.bcxmodern.block;
import net.kanawo.bcxmodern.BCxModern;
import net.kanawo.bcxmodern.gui.GuiHandler;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import buildcraft.api.events.BlockInteractionEvent;
import buildcraft.builders.BlockBuilder;
import buildcraft.builders.ItemConstructionMarker;
import buildcraft.core.lib.fluids.TankUtils;
import buildcraft.core.lib.utils.ResourceUtils;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class BlockEMCBuilder extends BlockBuilder {
private static final String TEXTURE_PREFIX = BCxModern.MODID + ":emcbuilderBlock";
public BlockEMCBuilder() {
super();
setBlockName("emcBuilder");
}
@Override
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEMCBuilder();
}
/**
* Same as BlockBuilder, but opens the EMC Builder GUI. BlockBuilder's version cannot be called because it opens
* BuildCraft's own Builder GUI.
*/
@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX,
float hitY, float hitZ) {
BlockInteractionEvent event = new BlockInteractionEvent(player, this);
FMLCommonHandler.instance()
.bus()
.post(event);
if (event.isCanceled()) {
return true;
}
if (player.isSneaking()) {
return false;
}
TileEntity tile = world.getTileEntity(x, y, z);
TileEMCBuilder builder = tile instanceof TileEMCBuilder ? (TileEMCBuilder) tile : null;
Item equipped = player.getCurrentEquippedItem() != null ? player.getCurrentEquippedItem()
.getItem() : null;
if (equipped instanceof ItemConstructionMarker) {
if (ItemConstructionMarker.linkStarted(player.getCurrentEquippedItem())) {
ItemConstructionMarker.link(player.getCurrentEquippedItem(), world, x, y, z);
}
return true;
}
boolean filledTank = builder != null
&& TankUtils.handleRightClick(builder, ForgeDirection.UNKNOWN, player, true, false);
if (!filledTank && !world.isRemote) {
player.openGui(BCxModern.instance, GuiHandler.EMC_BUILDER, world, x, y, z);
}
return true;
}
/** Textures are looked up as textures/blocks/emcbuilderBlock/{top,bottom,front,side,...}.png. */
@Override
@SideOnly(Side.CLIENT)
public String[] getIconBlockNames() {
return new String[] { TEXTURE_PREFIX };
}
@Override
@SideOnly(Side.CLIENT)
public void registerBlockIcons(IIconRegister register) {
super.registerBlockIcons(register);
// Without bottom.png BlockBuildCraft would fall back to a missing "default" texture. Use the left face
// (index 4), which resolves to side.png, instead.
String bottom = TEXTURE_PREFIX + "/bottom";
if (!ResourceUtils.resourceExists(ResourceUtils.iconToResourcePath(register, bottom))) {
icons[0][0] = icons[0][4];
}
}
}
@@ -0,0 +1,22 @@
package net.kanawo.bcxmodern.block;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import buildcraft.builders.schematics.SchematicBuilderLike;
/**
* Blueprint schematic that, like the Builder's, drops the build state, and also drops the owner, so a copy built by
* someone else does not draw on the original owner's EMC.
*/
public class SchematicEMCBuilder extends SchematicBuilderLike {
@Override
public void onNBTLoaded() {
super.onNBTLoaded();
if (tileNBT != null) {
tileNBT.removeTag(TileEMCBuilder.OWNER_MOST);
tileNBT.removeTag(TileEMCBuilder.OWNER_LEAST);
}
}
}
@@ -0,0 +1,107 @@
package net.kanawo.bcxmodern.gui;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import buildcraft.builders.gui.ContainerBuilder;
import buildcraft.core.lib.gui.widgets.ScrollbarWidget;
/**
* The Builder container plus an EMC consume slot. Extending {@link ContainerBuilder} keeps BuildCraft's requirement
* tooltips and GUI watcher handling.
*/
public class ContainerEMCBuilder extends ContainerBuilder {
/** Matches the slot frame at (132, 26) in emcbuilder.png. */
public static final int CONSUME_SLOT_X = 133;
public static final int CONSUME_SLOT_Y = 27;
private static final int PLAYER_SLOTS = 9 * 4;
/** Index of the consume slot; the builder and player slots are all before it. */
private final int consumeSlot;
public ContainerEMCBuilder(IInventory playerInventory, TileEMCBuilder builder) {
super(playerInventory, builder);
// A builder made from a blueprint has no owner yet; whoever opens it first takes it.
if (!builder.getWorldObj().isRemote && playerInventory instanceof InventoryPlayer) {
builder.claimOwner(((InventoryPlayer) playerInventory).player);
}
consumeSlot = inventorySlots.size();
addSlotToContainer(new SlotEMCConsume(builder, CONSUME_SLOT_X, CONSUME_SLOT_Y));
}
public ScrollbarWidget getScrollbar() {
return scrollbarWidget;
}
/**
* Same behaviour as BuildCraftContainer, which assumes the player inventory is the last 36 slots; the consume slot
* comes after it here. Shift-clicking never moves items into the consume slot.
*/
@Override
public ItemStack transferStackInSlot(EntityPlayer player, int slotIndex) {
if (slotIndex == consumeSlot) {
return null;
}
Slot slot = (Slot) inventorySlots.get(slotIndex);
if (slot == null || !slot.getHasStack()) {
return null;
}
int playerStart = consumeSlot - PLAYER_SLOTS;
int hotbarStart = consumeSlot - 9;
ItemStack stack = slot.getStack();
ItemStack original = stack.copy();
if (slotIndex >= playerStart) {
if (!shiftIntoBuilder(stack, playerStart)) {
boolean fromHotbar = slotIndex >= hotbarStart;
if (!shiftItemStack(
stack,
fromHotbar ? playerStart : hotbarStart,
fromHotbar ? hotbarStart : consumeSlot)) {
return null;
}
}
} else if (!shiftItemStack(stack, playerStart, consumeSlot)) {
return null;
}
slot.onSlotChange(stack, original);
if (stack.stackSize <= 0) {
slot.putStack(null);
} else {
slot.onSlotChanged();
}
if (stack.stackSize == original.stackSize) {
return null;
}
slot.onPickupFromSlot(player, stack);
return original;
}
private boolean shiftIntoBuilder(ItemStack stack, int builderSlots) {
for (int i = 0; i < builderSlots; i++) {
Slot slot = (Slot) inventorySlots.get(i);
if (slot.isItemValid(stack) && shiftItemStack(stack, i, i + 1)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,252 @@
package net.kanawo.bcxmodern.gui;
import java.util.ArrayList;
import java.util.List;
import net.kanawo.bcxmodern.BCxModern;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import org.lwjgl.opengl.GL11;
import buildcraft.BuildCraftCore;
import buildcraft.builders.gui.SlotBuilderRequirement;
import buildcraft.core.blueprints.RequirementItemStack;
import buildcraft.core.lib.fluids.Tank;
import buildcraft.core.lib.gui.AdvancedSlot;
import buildcraft.core.lib.gui.GuiAdvancedInterface;
import buildcraft.core.lib.network.command.CommandWriter;
import buildcraft.core.lib.network.command.PacketCommand;
import buildcraft.core.lib.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import moze_intel.projecte.utils.Constants;
/**
* BuildCraft's GuiBuilder with the EMC consume slot and the stored EMC added. GuiBuilder cannot be extended because it
* creates its own ContainerBuilder.
*/
public class GuiEMCBuilder extends GuiAdvancedInterface {
/** BuildCraft's builder.png with the EMC consume slot added. */
private static final ResourceLocation REGULAR_TEXTURE = new ResourceLocation(
BCxModern.MODID,
"textures/gui/emcbuilder.png");
private static final ResourceLocation BLUEPRINT_TEXTURE = new ResourceLocation(
"buildcraftbuilders:textures/gui/builder_blueprint.png");
private final TileEMCBuilder builder;
private GuiButton selectedButton;
public GuiEMCBuilder(IInventory playerInventory, TileEMCBuilder builder) {
super(new ContainerEMCBuilder(playerInventory, builder), builder, BLUEPRINT_TEXTURE);
this.builder = builder;
xSize = 256;
ySize = 225;
resetNullSlots(6 * 4);
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 4; ++j) {
slots.set(i * 4 + j, new SlotBuilderRequirement(this, 179 + j * 18, 18 + i * 18));
}
}
}
private ContainerEMCBuilder getContainerBuilder() {
return (ContainerEMCBuilder) getContainer();
}
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2) {
super.drawGuiContainerForegroundLayer(par1, par2);
drawCenteredString(StringUtils.localize("tile.emcBuilder.name"), 178 / 2, 16, 0x404040);
drawCenteredString(
StatCollector.translateToLocalFormatted(
"gui.bcxmodern.stored",
Constants.EMC_FORMATTER.format(builder.getStoredEmc())),
178 / 2,
51,
0x404040);
if (builder.getStackInSlot(0) != null) {
fontRendererObj.drawString(StringUtils.localize("gui.building.resources"), 8, 60, 0x404040);
fontRendererObj.drawString(StringUtils.localize("gui.inventory"), 8, ySize - 97, 0x404040);
fontRendererObj.drawString(StringUtils.localize("gui.needed"), 178, 7, 0x404040);
fontRendererObj.drawString(StringUtils.localize("gui.building.fluids"), 178, 133, 0x404040);
}
drawTooltips(par1, par2);
}
private void drawTooltips(int par1, int par2) {
int top = guiTop + 145;
for (int i = 0; i < builder.fluidTanks.length; i++) {
int left = guiLeft + 179 + 18 * i;
if (par1 >= left && par2 >= top && par1 < (left + 16) && par2 < (left + 47)) {
List<String> fluidTip = new ArrayList<>();
Tank tank = builder.fluidTanks[i];
if (tank.getFluid() != null && tank.getFluid().amount > 0) {
fluidTip.add(
tank.getFluid()
.getLocalizedName());
if (!BuildCraftCore.hideFluidNumbers) {
fluidTip.add(
EnumChatFormatting.GRAY + "" + EnumChatFormatting.ITALIC + tank.getFluid().amount + " mB");
}
} else {
fluidTip.add(StatCollector.translateToLocal("gui.fluidtank.empty"));
}
drawHoveringText(fluidTip, par1 - guiLeft, par2 - guiTop, fontRendererObj);
return;
}
}
drawTooltipForSlotAt(par1, par2);
}
@Override
@SuppressWarnings("unchecked")
protected void drawGuiContainerBackgroundLayer(float f, int x, int y) {
// Like GuiBuilder, super is not called because the GUI is resized dynamically.
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
boolean isBlueprint = builder.getStackInSlot(0) != null;
mc.renderEngine.bindTexture(REGULAR_TEXTURE);
drawTexturedModalRect(guiLeft, guiTop, 0, 0, 176, ySize);
mc.renderEngine.bindTexture(BLUEPRINT_TEXTURE);
if (isBlueprint) {
drawTexturedModalRect(guiLeft + 169, guiTop, 169, 0, 256 - 169, ySize);
}
List<RequirementItemStack> needs = builder.getNeededItems();
if (needs != null) {
if (needs.size() > slots.size()) {
getContainerBuilder().getScrollbar().hidden = false;
getContainerBuilder().getScrollbar()
.setLength((needs.size() - slots.size() + 3) / 4);
} else {
getContainerBuilder().getScrollbar().hidden = true;
}
int offset = getContainerBuilder().getScrollbar()
.getPosition() * 4;
for (int s = 0; s < slots.size(); s++) {
int ts = offset + s;
if (ts >= needs.size()) {
((SlotBuilderRequirement) slots.get(s)).stack = null;
} else {
((SlotBuilderRequirement) slots.get(s)).stack = needs.get(ts);
}
}
for (GuiButton b : (List<GuiButton>) buttonList) {
b.visible = true;
}
} else {
getContainerBuilder().getScrollbar().hidden = true;
for (AdvancedSlot slot : slots) {
((SlotBuilderRequirement) slot).stack = null;
}
for (GuiButton b : (List<GuiButton>) buttonList) {
b.visible = false;
}
}
drawWidgets(x, y);
if (isBlueprint) {
drawBackgroundSlots(x, y);
for (int i = 0; i < builder.fluidTanks.length; i++) {
Tank tank = builder.fluidTanks[i];
if (tank.getFluid() != null && tank.getFluid().amount > 0) {
drawFluid(tank.getFluid(), guiLeft + 179 + 18 * i, guiTop + 145, 16, 47, tank.getCapacity());
}
}
mc.renderEngine.bindTexture(BLUEPRINT_TEXTURE);
for (int i = 0; i < builder.fluidTanks.length; i++) {
Tank tank = builder.fluidTanks[i];
if (tank.getFluid() != null && tank.getFluid().amount > 0) {
drawTexturedModalRect(guiLeft + 179 + 18 * i, guiTop + 145, 0, 54, 16, 47);
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public void initGui() {
super.initGui();
for (int i = 0; i < 4; i++) {
buttonList.add(new EraseTankButton(i, guiLeft + 178 + 18 * i, guiTop + 197, 18, 18));
}
}
@Override
protected void mouseMovedOrUp(int mouseX, int mouseY, int eventType) {
super.mouseMovedOrUp(mouseX, mouseY, eventType);
if (this.selectedButton != null && eventType == 0) {
this.selectedButton.mouseReleased(mouseX, mouseY);
this.selectedButton = null;
}
}
/** Empties one of the builder's fluid tanks; same as GuiBuilder's erase button. */
private class EraseTankButton extends GuiButton {
private boolean clicked;
public EraseTankButton(int id, int x, int y, int width, int height) {
super(id, x, y, width, height, null);
}
@Override
public boolean mousePressed(Minecraft mc, int x, int y) {
if (super.mousePressed(mc, x, y)) {
selectedButton = this;
clicked = true;
BuildCraftCore.instance.sendToServer(new PacketCommand(builder, "eraseFluidTank", new CommandWriter() {
@Override
public void write(ByteBuf data) {
data.writeInt(id);
}
}));
return true;
} else {
return false;
}
}
@Override
public void mouseReleased(int x, int y) {
super.mouseReleased(x, y);
clicked = false;
}
@Override
public void drawButton(Minecraft mc, int x, int y) {
if (!visible) {
return;
}
// hovered
this.field_146123_n = x >= this.xPosition && y >= this.yPosition
&& x < this.xPosition + this.width
&& y < this.yPosition + this.height;
mc.renderEngine.bindTexture(BLUEPRINT_TEXTURE);
drawTexturedModalRect(xPosition, yPosition, 0, (clicked ? 1 : this.field_146123_n ? 2 : 0) * 18, 18, 18);
mouseDragged(mc, x, y);
}
}
}
@@ -0,0 +1,34 @@
package net.kanawo.bcxmodern.gui;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import cpw.mods.fml.common.network.IGuiHandler;
public class GuiHandler implements IGuiHandler {
public static final int EMC_BUILDER = 0;
@Override
public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) {
TileEMCBuilder builder = getBuilder(id, world, x, y, z);
return builder == null ? null : new ContainerEMCBuilder(player.inventory, builder);
}
@Override
public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) {
TileEMCBuilder builder = getBuilder(id, world, x, y, z);
return builder == null ? null : new GuiEMCBuilder(player.inventory, builder);
}
private static TileEMCBuilder getBuilder(int id, World world, int x, int y, int z) {
if (id != EMC_BUILDER || !world.blockExists(x, y, z)) {
return null;
}
TileEntity tile = world.getTileEntity(x, y, z);
return tile instanceof TileEMCBuilder ? (TileEMCBuilder) tile : null;
}
}
@@ -0,0 +1,41 @@
package net.kanawo.bcxmodern.gui;
import net.kanawo.bcxmodern.tile.TileEMCBuilder;
import net.minecraft.inventory.InventoryBasic;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import moze_intel.projecte.utils.EMCHelper;
/**
* Converts any item with an EMC value into EMC stored in the builder, like the Transmutation Table's consume slot.
* The slot never holds anything itself.
*/
public class SlotEMCConsume extends Slot {
private final TileEMCBuilder builder;
public SlotEMCConsume(TileEMCBuilder builder, int x, int y) {
super(new InventoryBasic("EMC", false, 1), 0, x, y);
this.builder = builder;
}
@Override
public boolean isItemValid(ItemStack stack) {
return stack != null && EMCHelper.doesItemHaveEmc(stack);
}
@Override
public void putStack(ItemStack stack) {
if (stack == null) {
return;
}
// The client discards the stack too, so it does not show an item the server never stored.
if (!builder.getWorldObj().isRemote) {
builder.consumeForEmc(stack);
}
onSlotChanged();
}
}
@@ -0,0 +1,93 @@
package net.kanawo.bcxmodern.tile;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraft.world.WorldSavedData;
import net.minecraft.world.storage.MapStorage;
import net.minecraftforge.common.util.Constants;
/**
* EMC stored for EMC Builders, one balance per player, saved with the world (data/bcxmodern_emc.dat). All
* dimensions of a world share the same storage, so every EMC Builder a player owns in the world draws from the same
* balance. Server side only.
*/
public class EmcPool extends WorldSavedData {
private static final String NAME = "bcxmodern_emc";
private static final double MAX_EMC = moze_intel.projecte.utils.Constants.TILE_MAX_EMC;
private final Map<UUID, Double> balances = new HashMap<>();
public EmcPool(String name) {
super(name);
}
public static EmcPool get(World world) {
MapStorage storage = world.mapStorage;
EmcPool pool = (EmcPool) storage.loadData(EmcPool.class, NAME);
if (pool == null) {
pool = new EmcPool(NAME);
storage.setData(NAME, pool);
}
return pool;
}
public double get(UUID player) {
Double emc = balances.get(player);
return emc == null ? 0 : emc;
}
/** Adds (or, with a negative amount, removes) EMC, keeping the balance between 0 and the maximum. */
public void add(UUID player, double amount) {
double emc = Math.min(MAX_EMC, Math.max(0, get(player) + amount));
if (emc == 0) {
balances.remove(player);
} else {
balances.put(player, emc);
}
markDirty();
}
@Override
public void readFromNBT(NBTTagCompound nbt) {
balances.clear();
NBTTagList list = nbt.getTagList("Players", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < list.tagCount(); i++) {
NBTTagCompound entry = list.getCompoundTagAt(i);
balances.put(
new UUID(entry.getLong("UUIDMost"), entry.getLong("UUIDLeast")),
Math.min(MAX_EMC, Math.max(0, entry.getDouble("EMC"))));
}
}
@Override
public void writeToNBT(NBTTagCompound nbt) {
NBTTagList list = new NBTTagList();
for (Map.Entry<UUID, Double> balance : balances.entrySet()) {
NBTTagCompound entry = new NBTTagCompound();
entry.setLong(
"UUIDMost",
balance.getKey()
.getMostSignificantBits());
entry.setLong(
"UUIDLeast",
balance.getKey()
.getLeastSignificantBits());
entry.setDouble("EMC", balance.getValue());
list.appendTag(entry);
}
nbt.setTag("Players", list);
}
}
@@ -0,0 +1,294 @@
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();
}
}
@@ -1,8 +0,0 @@
package net.kanawo.emcbuilder;
public class ClientProxy extends CommonProxy {
// Override CommonProxy methods here, if you want a different behaviour on the client (e.g. registering renders).
// Don't forget to call the super methods as well.
}
@@ -1,27 +0,0 @@
package net.kanawo.emcbuilder;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
public class CommonProxy {
// preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the
// GameRegistry." (Remove if not needed)
public void preInit(FMLPreInitializationEvent event) {
Config.synchronizeConfiguration(event.getSuggestedConfigurationFile());
EMCBuilder.LOG.info(Config.greeting);
EMCBuilder.LOG.info("I am EMCBuilder at version " + Tags.VERSION);
}
// load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed)
public void init(FMLInitializationEvent event) {}
// postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed)
public void postInit(FMLPostInitializationEvent event) {}
// register server commands in this event handler (Remove if not needed)
public void serverStarting(FMLServerStartingEvent event) {}
}
@@ -1,20 +0,0 @@
package net.kanawo.emcbuilder;
import java.io.File;
import net.minecraftforge.common.config.Configuration;
public class Config {
public static String greeting = "Hello World";
public static void synchronizeConfiguration(File configFile) {
Configuration configuration = new Configuration(configFile);
greeting = configuration.getString("greeting", Configuration.CATEGORY_GENERAL, greeting, "How shall I greet?");
if (configuration.hasChanged()) {
configuration.save();
}
}
}
@@ -1,46 +0,0 @@
package net.kanawo.emcbuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
@Mod(modid = EMCBuilder.MODID, version = "1.0.0", name = "EMCBuilder", acceptedMinecraftVersions = "[1.7.10]")
public class EMCBuilder {
public static final String MODID = "emcbuilder";
public static final Logger LOG = LogManager.getLogger(MODID);
@SidedProxy(clientSide = "net.kanawo.emcbuilder.ClientProxy", serverSide = "net.kanawo.emcbuilder.CommonProxy")
public static CommonProxy proxy;
@Mod.EventHandler
// preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the
// GameRegistry." (Remove if not needed)
public void preInit(FMLPreInitializationEvent event) {
proxy.preInit(event);
}
@Mod.EventHandler
// load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed)
public void init(FMLInitializationEvent event) {
proxy.init(event);
}
@Mod.EventHandler
// postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed)
public void postInit(FMLPostInitializationEvent event) {
proxy.postInit(event);
}
@Mod.EventHandler
// register server commands in this event handler (Remove if not needed)
public void serverStarting(FMLServerStartingEvent event) {
proxy.serverStarting(event);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) [year] [fullname] Copyright (c) 2026 agomizi1cm
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,2 @@
tile.emcBuilder.name=EMC Builder
gui.bcxmodern.stored=EMC: %s
@@ -0,0 +1,2 @@
tile.emcBuilder.name=EMCビルダー
gui.bcxmodern.stored=EMC: %s
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

+6 -6
View File
@@ -3,18 +3,18 @@
"modList": [{ "modList": [{
"modid": "${modId}", "modid": "${modId}",
"name": "${modName}", "name": "${modName}",
"description": "An example mod for Minecraft 1.7.10 with Forge focused on a stable setup.", "description": "A Minecraft 1.7.10 port of BCx, a BuildCraft x Equivalent Exchange addon from the 1.2.5 era. Adds the EMC Builder, a BuildCraft Builder that pays for its materials with EMC.",
"version": "${modVersion}", "version": "${modVersion}",
"mcversion": "${minecraftVersion}", "mcversion": "${minecraftVersion}",
"url": "https://github.com/SinTh0r4s/MyMod", "url": "",
"updateUrl": "", "updateUrl": "",
"authorList": ["SinTho0r4s"], "authorList": ["agomizi1cm"],
"credits": "", "credits": "Original BCx for Minecraft 1.2.5. Built on BuildCraft and ProjectE.",
"logoFile": "", "logoFile": "",
"screenshots": [], "screenshots": [],
"parent": "", "parent": "",
"requiredMods": [], "requiredMods": ["BuildCraft|Builders", "ProjectE"],
"dependencies": [], "dependencies": ["BuildCraft|Builders", "ProjectE"],
"dependants": [], "dependants": [],
"useDependencyInformation": false "useDependencyInformation": false
}] }]