Compare commits

...
Author SHA1 Message Date
agomizi1cmandClaude Opus 5.5 7899ce82f7 Add the CE quarries, a port of CE's improved BuildCraft quarries
Port CE's three quarries from BuildCraft 3.1.5 (Minecraft 1.2.5) to
BuildCraft 7.1. TileCEQuarry follows BuildCraft 7's TileQuarry, whose
state is private, with CE's changes:

- Twice the battery, so the arm moves faster.
- A configurable default area (64x64), mirrored when placed while
  sneaking, and a configurable lowest Y level (5).
- Lava no longer blocks the columns below it.
- The search continues from the last target and moves past each one
  once done, so fluids flowing back in do not hold the arm in place.
- No chunk loading.
- CEQ-3 removes lava sources; CEQ-4 turns fluids around the drill into
  glass and accepts five times the input.

Soft blocks are checked without BuildCraft's cache, which is stale in
chunks without players. The arm is a copy of EntityMechanicalArm, which
only works with BuildCraft's own quarry. The recipes keep CE's layout
with BuildCraft 7 items; GateRecipe checks the gates' material and
logic, which ShapedOreRecipe ignores. Textures come from CE.

Also give the EMC Builder a default.png for its back, sides and bottom
instead of substituting the bottom icon in code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 11:13:02 +09:00
agomizi1cmandClaude Opus 5.5 af0afcb98f Rename to CE-modern, a 1.7.10 port of CE
Position this mod as a port of CE instead of BCx.

- Rename the mod to CE-modern (modid cemodern) and the package to
  net.kanawo.cemodern; the main class is now CEModern.
- Move assets to assets/cemodern and rename the lang key and EMC save
  data to match.
- Describe the mod as a port of CE in the README and mcmod.info.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:22:15 +09:00
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
44 changed files with 2494 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.
+59
View File
@@ -0,0 +1,59 @@
# CE-modern
mod「CE」を 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(不定形)
### CE クアーリー
BuildCraft の Quarry の強化版です。動作は BuildCraft 7 の Quarry と同じく、フレームを建ててからアームで掘り、掘ったアイテムを隣接するチェストやパイプに送ります。
- **採掘強化型クアーリー(CEQ-2)**: バッテリーが BuildCraft の Quarry の 2 倍で、その分アームが速く動きます。
- マーカーがなければ、本体の後ろ側を 64x64 の範囲で掘ります。スニークしながら置くと、範囲が左右反転します。
- y=5 で止まるので、岩盤層は荒らしません。
- 溶岩はスキップするだけで、その下の列も掘ります。
- 前回の位置から探索を続けます。レンチで右クリックすると、掘り終えた後でも最初から掘り直します。
- チャンクロード機能はありません。
- **溶岩除去型クアーリー(CEQ-3)**: CEQ-2 の機能に加えて、範囲内の溶岩源を除去します。
- **液体対策型クアーリー(CEQ-4)**: CEQ-3 の機能に加えて、アームに隣接する水・溶岩・オイルなどの液体をガラスに変えながら掘ります。受電量は 5 倍です。
レシピ(CE と同じ配置で、素材を BuildCraft 7 のものに置き換えています):
- CEQ-2: 上段 鉄歯車・レッドストーン・鉄歯車 / 中段 金歯車・鉄歯車・金歯車 / 下段 ダイヤ歯車・Quarry・ダイヤ歯車
- CEQ-3: 上段 エンダーパール・タンク・エンダーパール / 中段 ダイヤチップセット・ポンプ・ダイヤチップセット / 下段 ダイヤ AND ゲート・CEQ-2・ダイヤ AND ゲート
- CEQ-4: 上段 ガラス×3 / 中段 水バケツ・CEQ-3・溶岩バケツ / 下段 タンク・鉄 AND ゲート・タンク
`config/cemodern.cfg` の `quarry` で、初期範囲の大きさ(`defaultArea`)、本体の位置(`defaultPosition`)、掘る深さ(`miningYmin`)を変えられます。
## 必要な Mod
- BuildCraft 7.1.x(Builders, Factory, Silicon, Transport)
- 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 = CE-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 = cemodern
# 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.cemodern
# 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.cemodern.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,53 @@
package net.kanawo.cemodern;
import java.io.File;
import net.minecraftforge.common.config.Configuration;
/** Settings read from config/cemodern.cfg. */
public final class CEConfig {
private static final String CATEGORY_QUARRY = "quarry";
/** Side length of the area a CE quarry digs when no marker is next to it. */
public static int quarryDefaultSize = 64;
/** Where the quarry sits along the edge of its default area, counted from one corner. */
public static int quarryDefaultPosition = 0;
/** Lowest Y level CE quarries dig down to. Bedrock is at 0 to 4. */
public static int quarryMinY = 5;
private CEConfig() {}
public static void load(File file) {
Configuration config = new Configuration(file);
quarryDefaultSize = config.getInt(
"defaultArea",
CATEGORY_QUARRY,
quarryDefaultSize,
3,
130,
"Side length of the area a CE quarry digs when no marker is next to it.");
quarryDefaultPosition = config.getInt(
"defaultPosition",
CATEGORY_QUARRY,
quarryDefaultPosition,
0,
129,
"Where the quarry sits along the edge of its default area, counted from one corner."
+ " Sneak while placing it to count from the other corner.");
quarryMinY = config.getInt(
"miningYmin",
CATEGORY_QUARRY,
quarryMinY,
0,
255,
"Lowest Y level CE quarries dig down to. Bedrock is at 0 to 4.");
quarryDefaultPosition = Math.min(quarryDefaultPosition, quarryDefaultSize - 1);
if (config.hasChanged()) {
config.save();
}
}
}
@@ -0,0 +1,38 @@
package net.kanawo.cemodern;
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 = CEModern.MODID,
version = Tags.VERSION,
name = "CE-modern",
acceptedMinecraftVersions = "[1.7.10]",
dependencies = "required-after:BuildCraft|Builders;required-after:BuildCraft|Factory;"
+ "required-after:BuildCraft|Silicon;required-after:BuildCraft|Transport;required-after:ProjectE")
public class CEModern {
public static final String MODID = "cemodern";
public static final Logger LOG = LogManager.getLogger(MODID);
@Mod.Instance(MODID)
public static CEModern instance;
@SidedProxy(clientSide = "net.kanawo.cemodern.ClientProxy", serverSide = "net.kanawo.cemodern.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,30 @@
package net.kanawo.cemodern;
import net.kanawo.cemodern.entity.EntityCEQuarryArm;
import net.kanawo.cemodern.tile.TileCEQuarry;
import net.kanawo.cemodern.tile.TileEMCBuilder;
import buildcraft.core.lib.render.RenderVoid;
import buildcraft.core.render.RenderBuilder;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
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());
// The quarries draw their area box and the frames flying into place. The arm is drawn by its parts.
RenderBuilder quarryRenderer = new RenderBuilder();
ClientRegistry.bindTileEntitySpecialRenderer(TileCEQuarry.Fast.class, quarryRenderer);
ClientRegistry.bindTileEntitySpecialRenderer(TileCEQuarry.Lava.class, quarryRenderer);
ClientRegistry.bindTileEntitySpecialRenderer(TileCEQuarry.Fluid.class, quarryRenderer);
RenderingRegistry.registerEntityRenderingHandler(EntityCEQuarryArm.class, new RenderVoid());
}
}
@@ -0,0 +1,129 @@
package net.kanawo.cemodern;
import net.kanawo.cemodern.block.BlockCEQuarry;
import net.kanawo.cemodern.block.BlockEMCBuilder;
import net.kanawo.cemodern.block.SchematicEMCBuilder;
import net.kanawo.cemodern.gui.GuiHandler;
import net.kanawo.cemodern.recipe.GateRecipe;
import net.kanawo.cemodern.tile.QuarryType;
import net.kanawo.cemodern.tile.TileCEQuarry;
import net.kanawo.cemodern.tile.TileEMCBuilder;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.RecipeSorter;
import net.minecraftforge.oredict.RecipeSorter.Category;
import net.minecraftforge.oredict.ShapedOreRecipe;
import buildcraft.BuildCraftBuilders;
import buildcraft.BuildCraftFactory;
import buildcraft.api.blueprints.BuilderAPI;
import buildcraft.builders.schematics.SchematicBuilderLike;
import buildcraft.transport.gates.GateDefinition.GateLogic;
import buildcraft.transport.gates.GateDefinition.GateMaterial;
import buildcraft.transport.gates.ItemGate;
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 static BlockCEQuarry quarry2Block;
public static BlockCEQuarry quarry3Block;
public static BlockCEQuarry quarry4Block;
public void preInit(FMLPreInitializationEvent event) {
CEConfig.load(event.getSuggestedConfigurationFile());
emcBuilderBlock = new BlockEMCBuilder();
GameRegistry.registerBlock(emcBuilderBlock, "emcBuilder");
GameRegistry.registerTileEntity(TileEMCBuilder.class, CEModern.MODID + ":EMCBuilder");
quarry2Block = registerQuarry(QuarryType.FAST, TileCEQuarry.Fast.class);
quarry3Block = registerQuarry(QuarryType.LAVA, TileCEQuarry.Lava.class);
quarry4Block = registerQuarry(QuarryType.FLUID, TileCEQuarry.Fluid.class);
}
private static BlockCEQuarry registerQuarry(QuarryType type, Class<? extends TileCEQuarry> tile) {
BlockCEQuarry block = new BlockCEQuarry(type);
GameRegistry.registerBlock(block, type.name);
GameRegistry.registerTileEntity(tile, CEModern.MODID + ":" + type.name);
return block;
}
public void init(FMLInitializationEvent event) {
NetworkRegistry.INSTANCE.registerGuiHandler(CEModern.instance, new GuiHandler());
BuilderAPI.schematicRegistry.registerSchematicBlock(emcBuilderBlock, SchematicEMCBuilder.class);
BuilderAPI.schematicRegistry.registerSchematicBlock(quarry2Block, SchematicBuilderLike.class);
BuilderAPI.schematicRegistry.registerSchematicBlock(quarry3Block, SchematicBuilderLike.class);
BuilderAPI.schematicRegistry.registerSchematicBlock(quarry4Block, SchematicBuilderLike.class);
GameRegistry.addShapelessRecipe(
new ItemStack(emcBuilderBlock),
BuildCraftBuilders.builderBlock,
ObjHandler.transmuteStone);
RecipeSorter.register(CEModern.MODID + ":gate", GateRecipe.class, Category.SHAPED, "after:forge:shapedore");
// CE's recipes, with BuildCraft 7's items in place of BuildCraft 3's.
GameRegistry.addRecipe(
new ShapedOreRecipe(
new ItemStack(quarry2Block),
"iri",
"gig",
"dqd",
'r',
"dustRedstone",
'i',
"gearIron",
'g',
"gearGold",
'd',
"gearDiamond",
'q',
BuildCraftBuilders.quarryBlock));
GameRegistry.addRecipe(
new GateRecipe(
new ItemStack(quarry3Block),
GateMaterial.DIAMOND,
GateLogic.AND,
"ete",
"cpc",
"gqg",
'e',
Items.ender_pearl,
't',
BuildCraftFactory.tankBlock,
'p',
BuildCraftFactory.pumpBlock,
'q',
quarry2Block,
'c',
"chipsetDiamond",
'g',
ItemGate.makeGateItem(GateMaterial.DIAMOND, GateLogic.AND)));
GameRegistry.addRecipe(
new GateRecipe(
new ItemStack(quarry4Block),
GateMaterial.IRON,
GateLogic.AND,
"ggg",
"wql",
"tit",
'g',
"blockGlass",
'w',
Items.water_bucket,
'l',
Items.lava_bucket,
'q',
quarry3Block,
't',
BuildCraftFactory.tankBlock,
'i',
ItemGate.makeGateItem(GateMaterial.IRON, GateLogic.AND)));
}
}
@@ -0,0 +1,111 @@
package net.kanawo.cemodern.block;
import java.util.ArrayList;
import net.kanawo.cemodern.CEModern;
import net.kanawo.cemodern.tile.QuarryType;
import net.kanawo.cemodern.tile.TileCEQuarry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import buildcraft.BuildCraftBuilders;
import buildcraft.api.tools.IToolWrench;
import buildcraft.core.lib.block.BlockBuildCraft;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* A CE quarry. Like BuildCraft's quarry it faces away from the placer, digs the area behind it, restarts when
* wrenched, and takes its frame with it when broken.
* <p>
* Textures are looked up as textures/blocks/{quarry2,quarry3,quarry4}/{front,top,side}.png. The side texture is
* also used for the back and the bottom.
*/
public class BlockCEQuarry extends BlockBuildCraft {
public final QuarryType type;
public BlockCEQuarry(QuarryType type) {
super(Material.iron);
this.type = type;
setHardness(10F);
setResistance(10F);
setStepSound(soundTypeAnvil);
setRotatable(true);
setBlockName("ce" + Character.toUpperCase(type.name.charAt(0)) + type.name.substring(1));
}
@Override
public TileEntity createNewTileEntity(World world, int metadata) {
return type.createTile();
}
/**
* Registers the icons directly. BlockBuildCraft's lookup would fall back to a missing "default" texture for the
* bottom, which has no texture of its own.
*/
@Override
@SideOnly(Side.CLIENT)
public void registerBlockIcons(IIconRegister register) {
String prefix = CEModern.MODID + ":" + type.name + "/";
IIcon side = register.registerIcon(prefix + "side");
// Indexed by side: bottom, top, front, back, left, right.
icons = new IIcon[16][];
icons[0] = new IIcon[] { side, register.registerIcon(prefix + "top"), register.registerIcon(prefix + "front"),
side, side, side };
}
@Override
public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune) {
if (BuildCraftBuilders.quarryOneTimeUse) {
return new ArrayList<ItemStack>();
}
return super.getDrops(world, x, y, z, metadata, fortune);
}
@Override
public void breakBlock(World world, int x, int y, int z, Block block, int metadata) {
if (world.isRemote) {
return;
}
BuildCraftBuilders.frameBlock.removeNeighboringFrames(world, x, y, z);
super.breakBlock(world, x, y, z, block, metadata);
}
@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX,
float hitY, float hitZ) {
if (super.onBlockActivated(world, x, y, z, player, side, hitX, hitY, hitZ)) {
return true;
}
if (player.isSneaking()) {
return false;
}
// Restart the quarry if it is a wrench.
Item equipped = player.getCurrentEquippedItem() != null ? player.getCurrentEquippedItem()
.getItem() : null;
TileEntity tile = world.getTileEntity(x, y, z);
if (tile instanceof TileCEQuarry && equipped instanceof IToolWrench
&& ((IToolWrench) equipped).canWrench(player, x, y, z)) {
((TileCEQuarry) tile).reinitialize();
((IToolWrench) equipped).wrenchUsed(player, x, y, z);
return true;
}
return false;
}
}
@@ -0,0 +1,84 @@
package net.kanawo.cemodern.block;
import net.kanawo.cemodern.CEModern;
import net.kanawo.cemodern.gui.GuiHandler;
import net.kanawo.cemodern.tile.TileEMCBuilder;
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 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 = CEModern.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(CEModern.instance, GuiHandler.EMC_BUILDER, world, x, y, z);
}
return true;
}
/**
* Textures are looked up as textures/blocks/emcbuilderBlock/{top,front,default}.png. default.png covers the faces
* without a texture of their own: the back, the left, the right and the bottom.
*/
@Override
@SideOnly(Side.CLIENT)
public String[] getIconBlockNames() {
return new String[] { TEXTURE_PREFIX };
}
}
@@ -0,0 +1,22 @@
package net.kanawo.cemodern.block;
import net.kanawo.cemodern.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,152 @@
package net.kanawo.cemodern.entity;
import net.kanawo.cemodern.tile.TileCEQuarry;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import buildcraft.builders.BuilderProxy;
import buildcraft.core.lib.EntityBlock;
/**
* The mechanical arm of a CE quarry, the same as BuildCraft's {@code EntityMechanicalArm}, which only works with
* BuildCraft's own quarry tile.
* <p>
* It only draws the arm: three beams and the drill head, made of BuildCraft's {@link EntityBlock}s. The quarry spawns
* one on each side and moves its head. The entity is not registered, so it is neither sent to clients nor saved.
*/
public class EntityCEQuarryArm extends Entity {
private TileCEQuarry parent;
private double armSizeX;
private double armSizeZ;
private double xRoot;
private double yRoot;
private double zRoot;
private int headX, headY, headZ;
private EntityBlock xArm, yArm, zArm, head;
public EntityCEQuarryArm(World world) {
super(world);
makeParts(world);
noClip = true;
}
public EntityCEQuarryArm(World world, double x, double y, double z, double width, double height,
TileCEQuarry parent) {
this(world);
setPositionAndRotation(parent.xCoord, parent.yCoord, parent.zCoord, 0, 0);
this.xRoot = x;
this.yRoot = y;
this.zRoot = z;
this.motionX = 0.0;
this.motionY = 0.0;
this.motionZ = 0.0;
setArmSize(width, height);
setHead(x, y - 2, z);
this.parent = parent;
parent.setArm(this);
updatePosition();
}
public void setHead(double x, double y, double z) {
this.headX = (int) (x * 32D);
this.headY = (int) (y * 32D);
this.headZ = (int) (z * 32D);
}
private void setArmSize(double x, double z) {
armSizeX = x;
xArm.iSize = x;
armSizeZ = z;
zArm.kSize = z;
updatePosition();
}
private void makeParts(World world) {
xArm = BuilderProxy.proxy.newDrill(world, 0, 0, 0, 1, 0.5, 0.5, true);
yArm = BuilderProxy.proxy.newDrill(world, 0, 0, 0, 0.5, 1, 0.5, false);
zArm = BuilderProxy.proxy.newDrill(world, 0, 0, 0, 0.5, 0.5, 1, true);
head = BuilderProxy.proxy.newDrillHead(world, 0, 0, 0, 0.2, 1, 0.2);
head.shadowSize = 1.0F;
world.spawnEntityInWorld(xArm);
world.spawnEntityInWorld(yArm);
world.spawnEntityInWorld(zArm);
world.spawnEntityInWorld(head);
}
@Override
protected void entityInit() {}
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
xRoot = nbt.getDouble("xRoot");
yRoot = nbt.getDouble("yRoot");
zRoot = nbt.getDouble("zRoot");
armSizeX = nbt.getDouble("armSizeX");
armSizeZ = nbt.getDouble("armSizeZ");
setArmSize(armSizeX, armSizeZ);
updatePosition();
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
nbt.setDouble("xRoot", xRoot);
nbt.setDouble("yRoot", yRoot);
nbt.setDouble("zRoot", zRoot);
nbt.setDouble("armSizeX", armSizeX);
nbt.setDouble("armSizeZ", armSizeZ);
}
private void findAndJoinQuarry() {
TileEntity te = worldObj.getTileEntity((int) posX, (int) posY, (int) posZ);
if (te instanceof TileCEQuarry) {
parent = (TileCEQuarry) te;
parent.setArm(this);
} else {
setDead();
}
}
@Override
public void onUpdate() {
super.onUpdate();
updatePosition();
if (parent == null) {
findAndJoinQuarry();
}
if (parent == null) {
setDead();
}
}
public void updatePosition() {
double headPosX = headX / 32D;
double headPosY = headY / 32D;
double headPosZ = headZ / 32D;
xArm.setPosition(xRoot, yRoot, headPosZ + 0.25);
yArm.jSize = yRoot - headPosY - 1;
yArm.setPosition(headPosX + 0.25, headPosY + 1, headPosZ + 0.25);
zArm.setPosition(headPosX + 0.25, yRoot, zRoot);
head.setPosition(headPosX + 0.4, headPosY, headPosZ + 0.4);
}
@Override
public void setDead() {
if (worldObj != null && worldObj.isRemote) {
xArm.setDead();
yArm.setDead();
zArm.setDead();
head.setDead();
}
super.setDead();
}
}
@@ -0,0 +1,107 @@
package net.kanawo.cemodern.gui;
import net.kanawo.cemodern.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.cemodern.gui;
import java.util.ArrayList;
import java.util.List;
import net.kanawo.cemodern.CEModern;
import net.kanawo.cemodern.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(
CEModern.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.cemodern.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.cemodern.gui;
import net.kanawo.cemodern.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.cemodern.gui;
import net.kanawo.cemodern.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,44 @@
package net.kanawo.cemodern.recipe;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.oredict.ShapedOreRecipe;
import buildcraft.transport.gates.GateDefinition.GateLogic;
import buildcraft.transport.gates.GateDefinition.GateMaterial;
import buildcraft.transport.gates.ItemGate;
/**
* A shaped recipe whose gates must be of one material and logic. Gates are told apart only by NBT, which
* {@link ShapedOreRecipe} ignores, so without this any gate would do. Expansions on the gate are allowed.
*/
public class GateRecipe extends ShapedOreRecipe {
private final GateMaterial material;
private final GateLogic logic;
/** The recipe arguments are those of {@link ShapedOreRecipe}; give the gate as {@code ItemGate.makeGateItem}. */
public GateRecipe(ItemStack result, GateMaterial material, GateLogic logic, Object... recipe) {
super(result, recipe);
this.material = material;
this.logic = logic;
}
@Override
public boolean matches(InventoryCrafting inv, World world) {
if (!super.matches(inv, world)) {
return false;
}
for (int i = 0; i < inv.getSizeInventory(); i++) {
ItemStack stack = inv.getStackInSlot(i);
if (stack != null && stack.getItem() instanceof ItemGate
&& (ItemGate.getMaterial(stack) != material || ItemGate.getLogic(stack) != logic)) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,93 @@
package net.kanawo.cemodern.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/cemodern_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 = "cemodern_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,34 @@
package net.kanawo.cemodern.tile;
/** The three CE quarries. They share {@link TileCEQuarry} and differ only in how they treat fluids. */
public enum QuarryType {
/** CEQ-2: a faster BuildCraft quarry with a bigger battery. Skips fluids like BuildCraft's does. */
FAST("quarry2", 1),
/** CEQ-3: also targets lava sources and removes them. */
LAVA("quarry3", 1),
/** CEQ-4: targets any fluid and turns fluids next to the drill into glass before digging. */
FLUID("quarry4", 5);
/** Block registry name, also the texture folder name. */
public final String name;
/** How many times BuildCraft's quarry input rate this quarry accepts per tick. */
final int receiveMultiplier;
QuarryType(String name, int receiveMultiplier) {
this.name = name;
this.receiveMultiplier = receiveMultiplier;
}
public TileCEQuarry createTile() {
switch (this) {
case LAVA:
return new TileCEQuarry.Lava();
case FLUID:
return new TileCEQuarry.Fluid();
case FAST:
default:
return new TileCEQuarry.Fast();
}
}
}
@@ -0,0 +1,890 @@
package net.kanawo.cemodern.tile;
import java.util.List;
import net.kanawo.cemodern.CEConfig;
import net.kanawo.cemodern.entity.EntityCEQuarryArm;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.IFluidBlock;
import buildcraft.BuildCraftBuilders;
import buildcraft.BuildCraftCore;
import buildcraft.api.blueprints.BuilderAPI;
import buildcraft.api.core.BuildCraftAPI;
import buildcraft.api.core.IAreaProvider;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.api.filler.FillerManager;
import buildcraft.api.statements.IStatementParameter;
import buildcraft.api.tiles.IControllable;
import buildcraft.api.tiles.IHasWork;
import buildcraft.api.transport.IPipeConnection;
import buildcraft.api.transport.IPipeTile;
import buildcraft.core.Box;
import buildcraft.core.Box.Kind;
import buildcraft.core.CoreConstants;
import buildcraft.core.DefaultProps;
import buildcraft.core.blueprints.Blueprint;
import buildcraft.core.blueprints.BptBuilderBase;
import buildcraft.core.blueprints.BptBuilderBlueprint;
import buildcraft.core.builders.TileAbstractBuilder;
import buildcraft.core.builders.patterns.FillerPattern;
import buildcraft.core.internal.IDropControlInventory;
import buildcraft.core.lib.RFBattery;
import buildcraft.core.lib.utils.BlockMiner;
import buildcraft.core.lib.utils.BlockUtils;
import buildcraft.core.lib.utils.Utils;
import buildcraft.core.proxy.CoreProxy;
import io.netty.buffer.ByteBuf;
/**
* The CE quarries, a port of CE's TileQuarry2 and TileQuarry4 for BuildCraft 1.2.5.
* <p>
* It works like BuildCraft's {@code TileQuarry}: it builds a frame, then moves a mechanical arm over the area and
* mines blocks with BuildCraft's {@link BlockMiner}. That class keeps its state private, so this one cannot extend it
* and follows it instead. What CE changed:
* <ul>
* <li>Twice the battery, so the arm, whose speed grows with the stored energy, moves faster.</li>
* <li>A configurable default area (64x64 by default), mirrored when the quarry is placed while sneaking.</li>
* <li>It stops at a configurable Y level (5 by default) instead of digging into the bedrock.</li>
* <li>Lava does not block the columns below it.</li>
* <li>It remembers where it is in the area, so each search continues from the last target instead of starting
* over.</li>
* <li>It does not load chunks.</li>
* <li>CEQ-3 removes lava sources, and CEQ-4 turns fluids around the drill into glass (see {@link QuarryType}).</li>
* </ul>
*/
public abstract class TileCEQuarry extends TileAbstractBuilder
implements IHasWork, ISidedInventory, IDropControlInventory, IPipeConnection, IControllable {
/** Tiles registered separately per type, so a quarry keeps its type when loaded from NBT. */
public static class Fast extends TileCEQuarry {
public Fast() {
super(QuarryType.FAST);
}
}
public static class Lava extends TileCEQuarry {
public Lava() {
super(QuarryType.LAVA);
}
}
public static class Fluid extends TileCEQuarry {
public Fluid() {
super(QuarryType.FLUID);
}
}
private enum Stage {
BUILDING,
DIGGING,
MOVING,
IDLE,
DONE
}
private enum ScanResult {
FOUND,
NONE,
PENDING
}
/** Blocks looked at per tick while searching, so an empty area does not stall a tick. */
private static final int SCAN_BUDGET = 4096;
/** RF to remove a lava source (CE: 100 MJ). */
private static final int LAVA_ERASE_ENERGY = 1000;
/** RF to turn one fluid block into glass (CE: 200 MJ). */
private static final int FLUID_REPLACE_ENERGY = 2000;
public final QuarryType type;
public EntityCEQuarryArm arm;
private final Box box = new Box();
private int targetX, targetY, targetZ;
private double headPosX, headPosY, headPosZ;
private double speed = 0.03;
private Stage stage = Stage.BUILDING;
private boolean movingHorizontally;
private boolean movingVertically;
private float headTrajectory;
/** True while the arm returns to its rest position after the last target. */
private boolean parking;
/**
* Next block to look at: a layer and an index into that layer's serpentine order. {@link Integer#MAX_VALUE}
* means start again from the top.
*/
private int scanY = Integer.MAX_VALUE;
private int scanIndex;
/** Whether the placer was sneaking, which mirrors the default area. */
private boolean mirrored;
private final SafeTimeTracker updateTracker = new SafeTimeTracker(BuildCraftCore.updateFactor);
private BptBuilderBase builder;
private boolean frameProducer = true;
private NBTTagCompound initNBT = null;
private BlockMiner miner;
protected TileCEQuarry(QuarryType type) {
this.type = type;
box.kind = Kind.STRIPES;
setBattery(
new RFBattery(
(int) (4 * 64 * BuilderAPI.BREAK_ENERGY * BuildCraftCore.miningMultiplier),
(int) (1000 * type.receiveMultiplier * BuildCraftCore.miningMultiplier),
0));
}
@Override
public void onBlockPlacedBy(EntityLivingBase entity, ItemStack stack) {
super.onBlockPlacedBy(entity, stack);
mirrored = entity.isSneaking();
}
private void createUtilsIfNeeded() {
if (!worldObj.isRemote && builder == null) {
if (!box.isInitialized()) {
setBoundaries();
}
initializeBlueprintBuilder();
}
if (stage != Stage.BUILDING) {
box.isVisible = false;
if (arm == null) {
createArm();
}
if (!worldObj.isRemote
&& (headPosX < box.xMin || headPosX > box.xMax || headPosZ < box.zMin || headPosZ > box.zMax)) {
setHead(box.xMin + 1, yCoord + 2, box.zMin + 1);
}
} else {
box.isVisible = true;
}
}
private void createArm() {
worldObj.spawnEntityInWorld(
new EntityCEQuarryArm(
worldObj,
box.xMin + CoreConstants.PIPE_MAX_POS,
yCoord + box.sizeY() - 1 + CoreConstants.PIPE_MIN_POS,
box.zMin + CoreConstants.PIPE_MAX_POS,
box.sizeX() - 2 + CoreConstants.PIPE_MIN_POS * 2,
box.sizeZ() - 2 + CoreConstants.PIPE_MIN_POS * 2,
this));
}
/** Callback from the arm once it is created. */
public void setArm(EntityCEQuarryArm arm) {
this.arm = arm;
}
/** The quarry does not load chunks, so it waits until every chunk of its area is loaded. */
private boolean areChunksLoaded() {
for (int chunkX = box.xMin >> 4; chunkX <= box.xMax >> 4; chunkX++) {
for (int chunkZ = box.zMin >> 4; chunkZ <= box.zMax >> 4; chunkZ++) {
if (!worldObj.blockExists(chunkX << 4, box.yMax, chunkZ << 4)) {
return false;
}
}
}
return true;
}
@Override
public void updateEntity() {
super.updateEntity();
if (worldObj.isRemote) {
if (stage != Stage.DONE) {
moveHead(speed);
}
return;
}
if (stage == Stage.DONE) {
if (mode == Mode.Loop) {
resetScan();
stage = Stage.IDLE;
} else {
return;
}
}
if (!areChunksLoaded()) {
return;
}
if (mode == Mode.Off && stage != Stage.MOVING) {
return;
}
createUtilsIfNeeded();
if (stage == Stage.BUILDING) {
if (builder != null && !builder.isDone(this)) {
builder.buildNextSlot(worldObj, this, xCoord, yCoord, zCoord);
} else {
stage = Stage.IDLE;
}
} else if (stage == Stage.DIGGING) {
dig();
} else if (stage == Stage.IDLE) {
// idling() sends its own network update when the stage changes.
idling();
return;
} else if (stage == Stage.MOVING) {
int energyUsed = getBattery()
.useEnergy(20, (int) Math.ceil(20D + (double) getBattery().getEnergyStored() / 10), false);
if (energyUsed >= 20) {
speed = 0.1 + energyUsed / 2000F;
moveHead(speed);
} else {
speed = 0;
}
}
if (updateTracker.markTimeIfDelay(worldObj)) {
sendNetworkUpdate();
}
}
private void dig() {
if (miner == null) {
stage = Stage.IDLE;
return;
}
int rfTaken = miner.acceptEnergy(getBattery().getEnergyStored());
getBattery().useEnergy(rfTaken, rfTaken, false);
// BlockMiner also reports hasMined() when the break event was cancelled, so check hasFailed() first.
if (miner.hasMined() && !miner.hasFailed()) {
// Collect items dropped around the head, such as the contents of a mined chest.
AxisAlignedBB around = AxisAlignedBB
.getBoundingBox(headPosX - 2, headPosY - 2, headPosZ - 2, headPosX + 3, headPosY + 3, headPosZ + 3);
@SuppressWarnings("unchecked")
List<EntityItem> items = worldObj.getEntitiesWithinAABB(EntityItem.class, around);
for (EntityItem entity : items) {
if (entity.isDead) {
continue;
}
ItemStack mineable = entity.getEntityItem();
if (mineable.stackSize <= 0) {
continue;
}
CoreProxy.proxy.removeEntity(entity);
miner.mineStack(mineable);
}
}
if (miner.hasMined() || miner.hasFailed()) {
miner = null;
scanIndex++;
if (!findFrame()) {
initializeBlueprintBuilder();
} else {
stage = Stage.IDLE;
}
}
}
/** Whether the frame next to the quarry is still there. If not, the quarry rebuilds it. */
private boolean findFrame() {
for (int i = 2; i < 6; i++) {
ForgeDirection o = ForgeDirection.getOrientation(i);
if (box.contains(xCoord + o.offsetX, yCoord + o.offsetY, zCoord + o.offsetZ)) {
return worldObj.getBlock(xCoord + o.offsetX, yCoord + o.offsetY, zCoord + o.offsetZ)
== BuildCraftBuilders.frameBlock;
}
}
return true;
}
private void idling() {
ScanResult result = parking ? ScanResult.NONE : findTarget();
if (result == ScanResult.PENDING) {
return;
}
if (result == ScanResult.FOUND) {
stage = Stage.MOVING;
} else {
int restX = box.xMin + 1, restY = yCoord + 2, restZ = box.zMin + 1;
if (headPosX == restX && headPosY == restY && headPosZ == restZ) {
parking = false;
stage = Stage.DONE;
} else {
setTarget(restX, restY, restZ);
parking = true;
stage = Stage.MOVING;
}
}
movingHorizontally = true;
movingVertically = true;
headTrajectory = (float) Math.atan2(targetZ - headPosZ, targetX - headPosX);
sendNetworkUpdate();
}
private void resetScan() {
scanY = Integer.MAX_VALUE;
scanIndex = 0;
}
/**
* Looks for the next block to dig, from the top layer down to {@link CEConfig#quarryMinY}. Each layer is walked
* in a serpentine order that starts where the layer above ended, and the search continues from where the last one
* stopped. If something is found, it becomes the target, and the search moves past it once the arm is done with it
* (see {@link #positionReached} and {@link #dig}). Each block is visited once per pass, so a fluid flowing back in
* behind the arm does not hold it in place.
*/
private ScanResult findTarget() {
int sizeX = box.sizeX() - 2;
int sizeZ = box.sizeZ() - 2;
int perLayer = sizeX * sizeZ;
int minY = Math.max(CEConfig.quarryMinY, 0);
int budget = SCAN_BUDGET;
if (scanY > yCoord + 3) {
scanY = yCoord + 3;
scanIndex = 0;
}
for (; scanY >= minY; scanY--, scanIndex = 0) {
for (; scanIndex < perLayer; scanIndex++) {
if (budget-- <= 0) {
return ScanResult.PENDING;
}
int row = scanIndex / sizeZ;
int column = scanIndex % sizeZ;
int searchX = (scanY & 1) == 0 ? row : sizeX - 1 - row;
int searchZ = (searchX & 1) == (scanY & 1) ? column : sizeZ - 1 - column;
int x = box.xMin + 1 + searchX;
int z = box.zMin + 1 + searchZ;
if (isTarget(x, scanY, z)) {
setTarget(x, scanY + 1, z);
return ScanResult.FOUND;
}
}
}
return ScanResult.NONE;
}
private boolean isTarget(int x, int y, int z) {
Block block = worldObj.getBlock(x, y, z);
return isFluidTarget(block, worldObj.getBlockMetadata(x, y, z)) || isMineable(block, x, y, z);
}
private boolean isMineable(Block block, int x, int y, int z) {
return BlockUtils.canChangeBlock(block, worldObj, x, y, z) && !isSoft(block, x, y, z)
&& !isFluid(block)
&& block != BuildCraftBuilders.frameBlock;
}
/**
* Same as {@link BuildCraftAPI#isSoftBlock}, without its cache. The cache is only updated when the block change is
* sent to players, so in a chunk without players a mined block would still read as solid.
*/
private boolean isSoft(Block block, int x, int y, int z) {
return block.isAir(worldObj, x, y, z) || BuildCraftAPI.softBlocks.contains(block)
|| block.isReplaceable(worldObj, x, y, z);
}
/** Fluids this quarry goes to even though it cannot mine them. */
private boolean isFluidTarget(Block block, int meta) {
switch (type) {
case LAVA:
return block instanceof BlockLiquid && block.getMaterial() == Material.lava && meta == 0;
case FLUID:
return isFluid(block);
case FAST:
default:
return false;
}
}
private static boolean isFluid(Block block) {
return block instanceof BlockLiquid || block instanceof IFluidBlock;
}
@Override
public void readFromNBT(NBTTagCompound nbt) {
super.readFromNBT(nbt);
if (nbt.hasKey("box")) {
box.initialize(nbt.getCompoundTag("box"));
}
targetX = nbt.getInteger("targetX");
targetY = nbt.getInteger("targetY");
targetZ = nbt.getInteger("targetZ");
headPosX = nbt.getDouble("headPosX");
headPosY = nbt.getDouble("headPosY");
headPosZ = nbt.getDouble("headPosZ");
if (nbt.hasKey("scanY")) {
scanY = nbt.getInteger("scanY");
scanIndex = nbt.getInteger("scanIndex");
}
parking = nbt.getBoolean("parking");
// The build state is loaded in initialize(), once the frame blueprint exists.
initNBT = (NBTTagCompound) nbt.getCompoundTag("bpt")
.copy();
}
@Override
public void writeToNBT(NBTTagCompound nbt) {
super.writeToNBT(nbt);
nbt.setInteger("targetX", targetX);
nbt.setInteger("targetY", targetY);
nbt.setInteger("targetZ", targetZ);
nbt.setDouble("headPosX", headPosX);
nbt.setDouble("headPosY", headPosY);
nbt.setDouble("headPosZ", headPosZ);
nbt.setInteger("scanY", scanY);
nbt.setInteger("scanIndex", scanIndex);
nbt.setBoolean("parking", parking);
NBTTagCompound boxTag = new NBTTagCompound();
box.writeToNBT(boxTag);
nbt.setTag("box", boxTag);
NBTTagCompound bptNBT = new NBTTagCompound();
if (builder != null) {
NBTTagCompound builderCpt = new NBTTagCompound();
builder.saveBuildStateToNBT(builderCpt, this);
bptNBT.setTag("builderState", builderCpt);
}
nbt.setTag("bpt", bptNBT);
}
/** Called by {@link #moveHead} when the arm arrives at its target. */
private void positionReached() {
if (worldObj.isRemote) {
return;
}
if (parking) {
parking = false;
stage = Stage.DONE;
return;
}
int x = targetX;
int y = targetY - 1;
int z = targetZ;
if (type == QuarryType.FLUID) {
// The target itself becomes glass and is then mined. Its neighbours stay glass, walling off the shaft.
replaceFluid(x, y, z);
replaceFluid(x + 1, y, z);
replaceFluid(x - 1, y, z);
replaceFluid(x, y, z + 1);
replaceFluid(x, y, z - 1);
replaceFluid(x, y + 1, z);
}
Block block = worldObj.getBlock(x, y, z);
if (type == QuarryType.LAVA && isFluidTarget(block, worldObj.getBlockMetadata(x, y, z))) {
worldObj.setBlockToAir(x, y, z);
useFluidEnergy(LAVA_ERASE_ENERGY);
scanIndex++;
stage = Stage.IDLE;
} else if (isMineable(block, x, y, z)) {
// dig() moves the search on once the block is mined.
miner = new BlockMiner(worldObj, this, x, y, z);
stage = Stage.DIGGING;
} else {
scanIndex++;
stage = Stage.IDLE;
}
}
private void replaceFluid(int x, int y, int z) {
if (worldObj.blockExists(x, y, z) && isFluid(worldObj.getBlock(x, y, z))) {
worldObj.setBlock(x, y, z, Blocks.glass);
useFluidEnergy(FLUID_REPLACE_ENERGY);
}
}
/** Takes what it can of the cost; like CE, a missing remainder does not stop the work. */
private void useFluidEnergy(int energy) {
int cost = (int) (energy * BuildCraftCore.miningMultiplier);
getBattery().useEnergy(0, cost, false);
}
@Override
protected int getNetworkUpdateRange() {
return DefaultProps.NETWORK_UPDATE_RANGE
+ (int) Math.ceil(Math.sqrt(yCoord * yCoord + box.sizeX() * box.sizeX() + box.sizeZ() * box.sizeZ()));
}
@Override
public void invalidate() {
super.invalidate();
destroy();
}
@Override
public void onChunkUnload() {
destroy();
}
@Override
public void destroy() {
if (arm != null) {
arm.setDead();
}
arm = null;
frameProducer = false;
if (miner != null) {
miner.invalidate();
}
}
@Override
public boolean hasWork() {
return stage != Stage.DONE;
}
/**
* Uses the marker next to the quarry if it spans at least 3x3, or else the default area from the config. The
* default area lies behind the quarry, with the quarry on its near edge.
*/
private void setBoundaries() {
IAreaProvider a = Utils.getNearbyAreaProvider(worldObj, xCoord, yCoord, zCoord);
if (a != null && (a.xMax() - a.xMin() + 1 < 3 || a.zMax() - a.zMin() + 1 < 3)) {
a = null;
}
if (a != null) {
box.initialize(a);
if (box.sizeY() < 5) {
box.yMax = box.yMin + 4;
}
a.removeFromWorld();
} else {
int size = CEConfig.quarryDefaultSize;
int before = mirrored ? size - CEConfig.quarryDefaultPosition - 1 : CEConfig.quarryDefaultPosition;
int after = size - before - 1;
int far = size - 1;
int xMin, zMin;
switch (worldObj.getBlockMetadata(xCoord, yCoord, zCoord) & 7) {
case 3:
xMin = xCoord - before;
zMin = zCoord - far - 1;
break;
case 4:
xMin = xCoord + 1;
zMin = zCoord - before;
break;
case 5:
xMin = xCoord - far - 1;
zMin = zCoord - after;
break;
case 2:
default:
xMin = xCoord - after;
zMin = zCoord + 1;
break;
}
box.initialize(xMin, yCoord, zMin, xMin + far, yCoord + 4, zMin + far);
}
sendNetworkUpdate();
}
/** Starts building the frame. Already placed frame blocks are kept. */
private void initializeBlueprintBuilder() {
Blueprint bpt = ((FillerPattern) FillerManager.registry.getPattern("buildcraft:frame"))
.getBlueprint(box, worldObj, new IStatementParameter[0], BuildCraftBuilders.frameBlock, 0);
if (bpt != null) {
builder = new BptBuilderBlueprint(bpt, worldObj, box.xMin, yCoord, box.zMin);
speed = 0;
stage = Stage.BUILDING;
sendNetworkUpdate();
}
}
@Override
public void writeData(ByteBuf stream) {
super.writeData(stream);
box.writeData(stream);
stream.writeInt(targetX);
stream.writeShort(targetY);
stream.writeInt(targetZ);
stream.writeDouble(headPosX);
stream.writeDouble(headPosY);
stream.writeDouble(headPosZ);
stream.writeFloat((float) speed);
stream.writeFloat(headTrajectory);
int flags = stage.ordinal();
flags |= movingHorizontally ? 0x10 : 0;
flags |= movingVertically ? 0x20 : 0;
stream.writeByte(flags);
}
@Override
public void readData(ByteBuf stream) {
super.readData(stream);
box.readData(stream);
targetX = stream.readInt();
targetY = stream.readUnsignedShort();
targetZ = stream.readInt();
headPosX = stream.readDouble();
headPosY = stream.readDouble();
headPosZ = stream.readDouble();
speed = stream.readFloat();
headTrajectory = stream.readFloat();
int flags = stream.readUnsignedByte();
stage = Stage.values()[flags & 0x07];
movingHorizontally = (flags & 0x10) != 0;
movingVertically = (flags & 0x20) != 0;
createUtilsIfNeeded();
if (arm != null) {
arm.setHead(headPosX, headPosY, headPosZ);
arm.updatePosition();
}
}
@Override
public void initialize() {
super.initialize();
if (!worldObj.isRemote && !box.initialized) {
setBoundaries();
}
createUtilsIfNeeded();
if (initNBT != null && builder != null) {
builder.loadBuildStateToNBT(initNBT.getCompoundTag("builderState"), this);
}
initNBT = null;
sendNetworkUpdate();
}
/** Rebuilds the frame and searches the whole area again, also after the quarry has finished. */
public void reinitialize() {
if (miner != null) {
miner.invalidate();
miner = null;
}
parking = false;
resetScan();
initializeBlueprintBuilder();
}
// The inventory holds an endless supply of frames for building the frame, and nothing else. It is hidden from
// pipes and hoppers by the sided inventory methods below.
@Override
public int getSizeInventory() {
return 1;
}
@Override
public ItemStack getStackInSlot(int i) {
return frameProducer ? new ItemStack(BuildCraftBuilders.frameBlock) : null;
}
@Override
public ItemStack decrStackSize(int i, int j) {
return frameProducer ? new ItemStack(BuildCraftBuilders.frameBlock, j) : null;
}
@Override
public void setInventorySlotContents(int i, ItemStack itemstack) {}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
return null;
}
@Override
public String getInventoryName() {
return "";
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 0;
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
return false;
}
@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer) {
return false;
}
@Override
public void openInventory() {}
@Override
public void closeInventory() {}
@Override
public boolean isBuildingMaterialSlot(int i) {
return true;
}
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return new int[] {};
}
@Override
public boolean canInsertItem(int slot, ItemStack stack, int side) {
return false;
}
@Override
public boolean canExtractItem(int slot, ItemStack stack, int side) {
return false;
}
private void moveHead(double instantSpeed) {
if (movingHorizontally) {
if (Math.abs(targetX - headPosX) < instantSpeed * 2 && Math.abs(targetZ - headPosZ) < instantSpeed * 2) {
headPosX = targetX;
headPosZ = targetZ;
movingHorizontally = false;
if (!movingVertically) {
positionReached();
headPosY = targetY;
}
} else {
headPosX += MathHelper.cos(headTrajectory) * instantSpeed;
headPosZ += MathHelper.sin(headTrajectory) * instantSpeed;
}
}
if (movingVertically) {
if (Math.abs(targetY - headPosY) < instantSpeed * 2) {
headPosY = targetY;
movingVertically = false;
if (!movingHorizontally) {
positionReached();
headPosX = targetX;
headPosZ = targetZ;
}
} else if (targetY > headPosY) {
headPosY += instantSpeed;
} else {
headPosY -= instantSpeed;
}
}
if (arm != null && worldObj.isRemote) {
arm.setHead(headPosX, headPosY, headPosZ);
arm.updatePosition();
}
}
private void setHead(double x, double y, double z) {
headPosX = x;
headPosY = y;
headPosZ = z;
}
private void setTarget(int x, int y, int z) {
targetX = x;
targetY = y;
targetZ = z;
}
@Override
public AxisAlignedBB getRenderBoundingBox() {
return new Box(this).extendToEncompass(box)
.expand(50)
.getBoundingBox();
}
@Override
public Box getBox() {
return box;
}
@Override
public boolean acceptsControlMode(Mode mode) {
return mode == Mode.Off || mode == Mode.On || mode == Mode.Loop;
}
@Override
public boolean doDrop() {
return false;
}
@Override
public ConnectOverride overridePipeConnection(IPipeTile.PipeType type, ForgeDirection with) {
return type == IPipeTile.PipeType.ITEM ? ConnectOverride.CONNECT : ConnectOverride.DEFAULT;
}
}
@@ -0,0 +1,294 @@
package net.kanawo.cemodern.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,5 @@
tile.emcBuilder.name=EMC Builder
gui.cemodern.stored=EMC: %s
tile.ceQuarry2.name=Quarry of fast mining type
tile.ceQuarry3.name=Quarry of lava removal type
tile.ceQuarry4.name=Quarry of fluid replace type
@@ -0,0 +1,5 @@
tile.emcBuilder.name=EMCビルダー
gui.cemodern.stored=EMC: %s
tile.ceQuarry2.name=採掘強化型クアーリー
tile.ceQuarry3.name=溶岩除去型クアーリー
tile.ceQuarry4.name=液体対策型クアーリー
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: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

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 CE. Adds the EMC Builder, a BuildCraft Builder that pays for its materials with EMC, and the CE quarries, improved BuildCraft quarries.",
"version": "${modVersion}", "version": "${modVersion}",
"mcversion": "${minecraftVersion}", "mcversion": "${minecraftVersion}",
"url": "https://github.com/SinTh0r4s/MyMod", "url": "",
"updateUrl": "", "updateUrl": "",
"authorList": ["SinTho0r4s"], "authorList": ["agomizi1cm"],
"credits": "", "credits": "Original CE. Built on BuildCraft and ProjectE.",
"logoFile": "", "logoFile": "",
"screenshots": [], "screenshots": [],
"parent": "", "parent": "",
"requiredMods": [], "requiredMods": ["BuildCraft|Builders", "BuildCraft|Factory", "BuildCraft|Silicon", "BuildCraft|Transport", "ProjectE"],
"dependencies": [], "dependencies": ["BuildCraft|Builders", "BuildCraft|Factory", "BuildCraft|Silicon", "BuildCraft|Transport", "ProjectE"],
"dependants": [], "dependants": [],
"useDependencyInformation": false "useDependencyInformation": false
}] }]