From 2e00964514193d4bf44055709cd2663c64966e56 Mon Sep 17 00:00:00 2001 From: Boblet Date: Mon, 17 Nov 2025 16:10:19 +0100 Subject: [PATCH] ignition --- .../machine/fusion/MachineFusionKlystron.java | 15 ++ .../machine/fusion/MachineFusionTorus.java | 31 +++ .../container/ContainerFusionKlystron.java | 68 ++++++ .../hbm/inventory/gui/GUIFusionKlystron.java | 130 +++++++++++ .../com/hbm/inventory/gui/GUIFusionTorus.java | 13 +- .../hbm/module/machine/ModuleMachineBase.java | 9 +- .../module/machine/ModuleMachineFusion.java | 56 ++++- .../tileentity/RenderFusionKlystron.java | 5 +- .../render/tileentity/RenderFusionTorus.java | 15 +- .../fusion/TileEntityFusionKlystron.java | 203 +++++++++++++++++- .../machine/fusion/TileEntityFusionTorus.java | 148 ++++++++++++- .../java/com/hbm/uninos/UniNodespace.java | 6 + .../gui/reactors/gui_fusion_klystron.png | Bin 0 -> 3386 bytes 13 files changed, 669 insertions(+), 30 deletions(-) create mode 100644 src/main/java/com/hbm/inventory/container/ContainerFusionKlystron.java create mode 100644 src/main/java/com/hbm/inventory/gui/GUIFusionKlystron.java create mode 100644 src/main/resources/assets/hbm/textures/gui/reactors/gui_fusion_klystron.png diff --git a/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionKlystron.java b/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionKlystron.java index 1d9035fc0..b68a23f07 100644 --- a/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionKlystron.java +++ b/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionKlystron.java @@ -6,6 +6,7 @@ import com.hbm.tileentity.TileEntityProxyCombo; import com.hbm.tileentity.machine.fusion.TileEntityFusionKlystron; import net.minecraft.block.material.Material; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -22,6 +23,11 @@ public class MachineFusionKlystron extends BlockDummyable { if(meta >= 6) return new TileEntityProxyCombo().power().fluid(); return null; } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { + return super.standardOpenBehavior(world, x, y, z, player, 0); + } @Override public int[] getDimensions() { @@ -44,5 +50,14 @@ public class MachineFusionKlystron extends BlockDummyable { super.fillSpace(world, x, y, z, dir, o); MultiblockHandlerXR.fillSpace(world, x + dir.offsetX * o, y, z + dir.offsetZ * o, new int[] {4, -3, 4, 3, 1, 1}, this, dir); + + x += dir.offsetX * o; + z += dir.offsetZ * o; + + ForgeDirection rot = dir.getRotation(ForgeDirection.UP); + + this.makeExtra(world, x + dir.offsetX * 3, y + 2, z + dir.offsetZ * 3); + this.makeExtra(world, x + rot.offsetX * 2, y, z + rot.offsetZ * 2); + this.makeExtra(world, x - rot.offsetX * 2, y, z - rot.offsetZ * 2); } } diff --git a/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionTorus.java b/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionTorus.java index 674cf4a09..988fbaa6d 100644 --- a/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionTorus.java +++ b/src/main/java/com/hbm/blocks/machine/fusion/MachineFusionTorus.java @@ -161,5 +161,36 @@ public class MachineFusionTorus extends BlockDummyable { } } } + + // is that enough ports? + this.makeExtra(world, x, y + 4, z); + + this.makeExtra(world, x + 6, y, z); + this.makeExtra(world, x + 6, y + 4, z); + this.makeExtra(world, x + 6, y, z + 2); + this.makeExtra(world, x + 6, y + 4, z + 2); + this.makeExtra(world, x + 6, y, z - 2); + this.makeExtra(world, x + 6, y + 4, z - 2); + + this.makeExtra(world, x - 6, y, z); + this.makeExtra(world, x - 6, y + 4, z); + this.makeExtra(world, x - 6, y, z + 2); + this.makeExtra(world, x - 6, y + 4, z + 2); + this.makeExtra(world, x - 6, y, z - 2); + this.makeExtra(world, x - 6, y + 4, z - 2); + + this.makeExtra(world, x, y, z + 6); + this.makeExtra(world, x, y + 4, z + 6); + this.makeExtra(world, x + 2, y, z + 6); + this.makeExtra(world, x + 2, y + 4, z + 6); + this.makeExtra(world, x - 2, y, z + 6); + this.makeExtra(world, x - 2, y + 4, z + 6); + + this.makeExtra(world, x, y, z - 6); + this.makeExtra(world, x, y + 4, z - 6); + this.makeExtra(world, x + 2, y, z - 6); + this.makeExtra(world, x + 2, y + 4, z - 6); + this.makeExtra(world, x - 2, y, z - 6); + this.makeExtra(world, x - 2, y + 4, z - 6); } } diff --git a/src/main/java/com/hbm/inventory/container/ContainerFusionKlystron.java b/src/main/java/com/hbm/inventory/container/ContainerFusionKlystron.java new file mode 100644 index 000000000..ae0c1233b --- /dev/null +++ b/src/main/java/com/hbm/inventory/container/ContainerFusionKlystron.java @@ -0,0 +1,68 @@ +package com.hbm.inventory.container; + +import com.hbm.inventory.SlotNonRetarded; +import com.hbm.items.ModItems; +import com.hbm.tileentity.machine.fusion.TileEntityFusionKlystron; + +import api.hbm.energymk2.IBatteryItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +public class ContainerFusionKlystron extends Container { + + protected TileEntityFusionKlystron klystron; + + public ContainerFusionKlystron(InventoryPlayer invPlayer, TileEntityFusionKlystron tedf) { + this.klystron = tedf; + + this.addSlotToContainer(new SlotNonRetarded(klystron, 0, 8, 72)); + + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 9; j++) { + this.addSlotToContainer(new Slot(invPlayer, j + i * 9 + 9, 17 + j * 18, 118 + i * 18)); + } + } + + for(int i = 0; i < 9; i++) { + this.addSlotToContainer(new Slot(invPlayer, i, 17 + i * 18, 176)); + } + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer player, int index) { + ItemStack copy = null; + Slot slot = (Slot) this.inventorySlots.get(index); + + if(slot != null && slot.getHasStack()) { + ItemStack stack = slot.getStack(); + copy = stack.copy(); + + if(index == 0) { + if(!this.mergeItemStack(stack, 1, this.inventorySlots.size(), true)) return null; + } else { + + if(copy.getItem() instanceof IBatteryItem || copy.getItem() == ModItems.battery_creative) { + if(!this.mergeItemStack(stack, 0, 1, false)) return null; + } else { + return null; + } + } + + if(stack.stackSize == 0) { + slot.putStack((ItemStack) null); + } else { + slot.onSlotChanged(); + } + } + + return copy; + } + + @Override + public boolean canInteractWith(EntityPlayer player) { + return klystron.isUseableByPlayer(player); + } +} diff --git a/src/main/java/com/hbm/inventory/gui/GUIFusionKlystron.java b/src/main/java/com/hbm/inventory/gui/GUIFusionKlystron.java new file mode 100644 index 000000000..88b74d3e6 --- /dev/null +++ b/src/main/java/com/hbm/inventory/gui/GUIFusionKlystron.java @@ -0,0 +1,130 @@ +package com.hbm.inventory.gui; + +import org.apache.commons.lang3.math.NumberUtils; +import org.lwjgl.input.Keyboard; + +import com.hbm.inventory.container.ContainerFusionKlystron; +import com.hbm.lib.RefStrings; +import com.hbm.packet.PacketDispatcher; +import com.hbm.packet.toserver.NBTControlPacket; +import com.hbm.render.util.GaugeUtil; +import com.hbm.tileentity.machine.fusion.TileEntityFusionKlystron; +import com.hbm.tileentity.machine.fusion.TileEntityFusionTorus; +import com.hbm.util.BobMathUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ResourceLocation; + +public class GUIFusionKlystron extends GuiInfoContainer { + + private static ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/reactors/gui_fusion_klystron.png"); + public TileEntityFusionKlystron klystron; + private GuiTextField field; + + public GUIFusionKlystron(InventoryPlayer invPlayer, TileEntityFusionKlystron klystron) { + super(new ContainerFusionKlystron(invPlayer, klystron)); + this.klystron = klystron; + + this.xSize = 194; + this.ySize = 200; + } + + @Override + public void initGui() { + super.initGui(); + + Keyboard.enableRepeatEvents(true); + this.field = new GuiTextField(this.fontRendererObj, guiLeft + 84, guiTop + 22, 102, 12); + this.field.setTextColor(0x00FF00); + this.field.setDisabledTextColour(0x00FF00); + this.field.setEnableBackgroundDrawing(false); + this.field.setText(klystron.outputTarget + ""); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float interp) { + super.drawScreen(mouseX, mouseY, interp); + + this.drawElectricityInfo(this, mouseX, mouseY, guiLeft + 8, guiTop + 18, 16, 52, klystron.power, klystron.getMaxPower()); + klystron.compair.renderTankInfo(this, mouseX, mouseY, guiLeft + 76, guiTop + 71, 18, 18); + } + + @Override + protected void mouseClicked(int i, int j, int button) { + super.mouseClicked(i, j, button); + + this.field.mouseClicked(i, j, button); + } + + protected void drawGuiContainerForegroundLayer(int i, int j) { + String name = this.klystron.hasCustomInventoryName() ? this.klystron.getInventoryName() : I18n.format(this.klystron.getInventoryName()); + this.fontRendererObj.drawString(name, 115 - this.fontRendererObj.getStringWidth(name) / 2, 6, 4210752); + this.fontRendererObj.drawString(I18n.format("container.inventory"), 35, this.ySize - 93, 4210752); + + String result = "= " + BobMathUtil.getShortNumber(klystron.outputTarget) + "KyU"; + if(klystron.outputTarget == klystron.MAX_OUTPUT) result += " (max)"; + this.fontRendererObj.drawString(result, 183 - this.fontRendererObj.getStringWidth(result), 40, 0x00FF00); + } + + protected void drawGuiContainerBackgroundLayer(float interp, int x, int y) { + Minecraft.getMinecraft().getTextureManager().bindTexture(texture); + drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); + + int p = (int) (klystron.power * 52 / klystron.getMaxPower()); + drawTexturedModalRect(guiLeft + 8, guiTop + 70 - p, 194, 52 - p, 16, p); + + double outputGauge = klystron.outputTarget <= 0 ? 0 : ((double) klystron.output / (double) klystron.outputTarget); + double airGauge = (double) klystron.compair.getFill() / (double) klystron.compair.getMaxFill(); + double powerGauge = TileEntityFusionTorus.getSpeedScaled(klystron.maxPower, klystron.power); + + // power LED + if(powerGauge >= 0.5 && klystron.output > 0) drawTexturedModalRect(guiLeft + 160, guiTop + 71, 210, 8, 8, 8); + else if(powerGauge < 0.5 && klystron.output > 0) drawTexturedModalRect(guiLeft + 160, guiTop + 71, 210, 0, 8, 8); + // cooling LED + if(airGauge >= 0.5 && klystron.output > 0) drawTexturedModalRect(guiLeft + 170, guiTop + 71, 210, 8, 8, 8); + else if(airGauge < 0.5 && klystron.output > 0) drawTexturedModalRect(guiLeft + 170, guiTop + 71, 210, 0, 8, 8); + // action LED + if(klystron.output >= klystron.outputTarget) drawTexturedModalRect(guiLeft + 180, guiTop + 71, 210, 8, 8, 8); + else if(klystron.output > 0) drawTexturedModalRect(guiLeft + 180, guiTop + 71, 210, 0, 8, 8); + + // output energy + GaugeUtil.drawSmoothGauge(guiLeft + 52, guiTop + 80, this.zLevel, outputGauge, 5, 2, 1, 0xA00000); + // air cooling + GaugeUtil.drawSmoothGauge(guiLeft + 88, guiTop + 80, this.zLevel, airGauge, 5, 2, 1, 0xA00000); + // power consumption + GaugeUtil.drawSmoothGauge(guiLeft + 124, guiTop + 80, this.zLevel, powerGauge, 5, 2, 1, 0xA00000); + + this.field.drawTextBox(); + } + + @Override + protected void keyTyped(char c, int key) { + + if(this.field.textboxKeyTyped(c, key)) { + + String text = this.field.getText(); + if(text.startsWith("0")) this.field.setText(text.substring(1)); + if(this.field.getText().isEmpty()) this.field.setText("0"); + if(NumberUtils.isDigits(this.field.getText())) { + long num = NumberUtils.toLong(this.field.getText()); + NBTTagCompound data = new NBTTagCompound(); + data.setLong("amount", num); + PacketDispatcher.wrapper.sendToServer(new NBTControlPacket(data, klystron.xCoord, klystron.yCoord, klystron.zCoord)); + } + + return; + } + + super.keyTyped(c, key); + } + + @Override + public void onGuiClosed() { + super.onGuiClosed(); + Keyboard.enableRepeatEvents(false); + } +} diff --git a/src/main/java/com/hbm/inventory/gui/GUIFusionTorus.java b/src/main/java/com/hbm/inventory/gui/GUIFusionTorus.java index 0c0cf3534..fe18029b4 100644 --- a/src/main/java/com/hbm/inventory/gui/GUIFusionTorus.java +++ b/src/main/java/com/hbm/inventory/gui/GUIFusionTorus.java @@ -65,7 +65,7 @@ public class GUIFusionTorus extends GuiInfoContainer { this.fontRendererObj.drawString(I18n.format("container.inventory"), 35, this.ySize - 93, 4210752); this.fontRendererObj.drawString(EnumChatFormatting.AQUA + "/123K", 136 + 54, 32, 4210752); - int heat = (int) Math.ceil(300); + int heat = (int) Math.ceil(torus.temperature); String label = (heat > 123 ? EnumChatFormatting.RED : EnumChatFormatting.AQUA) + "" + heat + "K"; this.fontRendererObj.drawString(label, 166 + 54 - this.fontRendererObj.getStringWidth(label), 22, 4210752); } @@ -112,14 +112,17 @@ public class GUIFusionTorus extends GuiInfoContainer { drawTexturedModalRect(guiLeft + 92, guiTop + 76, 246, 0, 3, 6); } - double gauge = BobMathUtil.sps((Minecraft.getMinecraft().theWorld.getTotalWorldTime() + interp) * 0.25) / 2 + 0.5D; + double demoGauge = BobMathUtil.sps((Minecraft.getMinecraft().theWorld.getTotalWorldTime() + interp) * 0.25) / 2 + 0.5D; + + double inputGauge = recipe == null ? 0 : Math.min(((double) torus.klystronEnergy / (double) recipe.ignitionTemp), 1.5) / 1.5D; + double outputGauge = recipe == null ? 0 : Math.min(((double) torus.plasmaEnergy / (double) recipe.outputTemp), 1.5) / 1.5D; // input energy - GaugeUtil.drawSmoothGauge(guiLeft + 52, guiTop + 124, this.zLevel, gauge, 5, 2, 1, 0xA00000); + GaugeUtil.drawSmoothGauge(guiLeft + 52, guiTop + 124, this.zLevel, inputGauge, 5, 2, 1, 0xA00000); // output genergy - GaugeUtil.drawSmoothGauge(guiLeft + 88, guiTop + 124, this.zLevel, gauge, 5, 2, 1, 0xA00000); + GaugeUtil.drawSmoothGauge(guiLeft + 88, guiTop + 124, this.zLevel, outputGauge, 5, 2, 1, 0xA00000); // fuel consumption - GaugeUtil.drawSmoothGauge(guiLeft + 124, guiTop + 124, this.zLevel, gauge, 5, 2, 1, 0xA00000); + GaugeUtil.drawSmoothGauge(guiLeft + 124, guiTop + 124, this.zLevel, torus.fuelConsumption, 5, 2, 1, 0xA00000); // recipe selector this.renderItem(recipe != null ? recipe.getIcon() : TEMPLATE_FOLDER, 44, 81); diff --git a/src/main/java/com/hbm/module/machine/ModuleMachineBase.java b/src/main/java/com/hbm/module/machine/ModuleMachineBase.java index bc4dfd83d..311a236fa 100644 --- a/src/main/java/com/hbm/module/machine/ModuleMachineBase.java +++ b/src/main/java/com/hbm/module/machine/ModuleMachineBase.java @@ -65,6 +65,13 @@ public abstract class ModuleMachineBase { if(power != 1 && battery.getPower() < recipe.power * power) return false; // only check with floating point numbers if mult is not 1 if(power == 1 && battery.getPower() < recipe.power) return false; + if(!hasInput(recipe)) return false; + + return canFitOutput(recipe); + } + + protected boolean hasInput(GenericRecipe recipe) { + if(recipe.inputItem != null) { for(int i = 0; i < Math.min(recipe.inputItem.length, inputSlots.length); i++) { if(!recipe.inputItem[i].matchesRecipe(slots[inputSlots[i]], false)) return false; @@ -77,7 +84,7 @@ public abstract class ModuleMachineBase { } } - return canFitOutput(recipe); + return true; } /** Whether the machine can hold the output produced by the recipe */ diff --git a/src/main/java/com/hbm/module/machine/ModuleMachineFusion.java b/src/main/java/com/hbm/module/machine/ModuleMachineFusion.java index 448469725..83424b63b 100644 --- a/src/main/java/com/hbm/module/machine/ModuleMachineFusion.java +++ b/src/main/java/com/hbm/module/machine/ModuleMachineFusion.java @@ -6,10 +6,13 @@ import com.hbm.inventory.recipes.loader.GenericRecipe; import com.hbm.inventory.recipes.loader.GenericRecipes; import api.hbm.energymk2.IEnergyHandlerMK2; +import io.netty.buffer.ByteBuf; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; public class ModuleMachineFusion extends ModuleMachineBase { - + + public double processSpeed = 1D; public double bonusSpeed = 0D; public double bonus; @@ -31,20 +34,39 @@ public class ModuleMachineFusion extends ModuleMachineBase { public ModuleMachineFusion fluidOutput(FluidTank a) { outputTanks[0] = a; return this; } // setup needs to run before update, used to keep track of things that ModuleMachineBase doesn't handle - public void preUpdate(double bonusSpeed) { + public void preUpdate(double processSpeed, double bonusSpeed) { + this.processSpeed = processSpeed; this.bonusSpeed = bonusSpeed; } + @Override + protected boolean hasInput(GenericRecipe recipe) { + + if(recipe.inputFluid != null) { + for(int i = 0; i < Math.min(recipe.inputFluid.length, inputTanks.length); i++) { + if(inputTanks[i].getFill() > 0 && inputTanks[i].getFill() < (int) Math.ceil(recipe.inputFluid[i].fill * processSpeed)) return false; + } + } + + return true; + } + @Override public void process(GenericRecipe recipe, double speed, double power) { - this.battery.setPower(this.battery.getPower() - (power == 1 ? recipe.power : (long) (recipe.power * power))); - double step = Math.min(speed / recipe.duration, 1D); // can't do more than one recipe per tick, might look into that later + this.battery.setPower(this.battery.getPower() - (long) Math.ceil((power == 1 ? recipe.power : (long) (recipe.power * power)) * processSpeed)); + double step = Math.min(speed / recipe.duration * processSpeed, 1D); // can't do more than one recipe per tick, might look into that later this.progress += step; this.bonus += step * this.bonusSpeed; this.bonus = Math.min(this.bonus, 1.5D); // bonus might not be used immediately in rare circumstances, allow 50% buffer + // fusion reactor is the only machine as of now that consumes input while not having finished the output + if(recipe.inputFluid != null) { + for(int i = 0; i < Math.min(recipe.inputFluid.length, inputTanks.length); i++) { + inputTanks[i].setFill(inputTanks[i].getFill() - (int) Math.ceil(recipe.inputFluid[i].fill * processSpeed)); + } + } + if(this.progress >= 1D) { - consumeInput(recipe); produceItem(recipe); if(this.canProcess(recipe, speed, power)) this.progress -= 1D; @@ -56,4 +78,28 @@ public class ModuleMachineFusion extends ModuleMachineBase { this.bonus -= 1D; } } + + @Override + public void serialize(ByteBuf buf) { + super.serialize(buf); + buf.writeDouble(bonus); + } + + @Override + public void deserialize(ByteBuf buf) { + super.deserialize(buf); + this.bonus = buf.readDouble(); + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + this.bonus = nbt.getDouble("bonus" + index); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setDouble("bonus" + index, bonus); + } } diff --git a/src/main/java/com/hbm/render/tileentity/RenderFusionKlystron.java b/src/main/java/com/hbm/render/tileentity/RenderFusionKlystron.java index a3b242000..0982d541d 100644 --- a/src/main/java/com/hbm/render/tileentity/RenderFusionKlystron.java +++ b/src/main/java/com/hbm/render/tileentity/RenderFusionKlystron.java @@ -6,6 +6,7 @@ import com.hbm.blocks.BlockDummyable; import com.hbm.blocks.ModBlocks; import com.hbm.main.ResourceManager; import com.hbm.render.item.ItemRenderBase; +import com.hbm.tileentity.machine.fusion.TileEntityFusionKlystron; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.item.Item; @@ -28,6 +29,8 @@ public class RenderFusionKlystron extends TileEntitySpecialRenderer implements I case 5: GL11.glRotatef(0, 0F, 1F, 0F); break; } + TileEntityFusionKlystron klystron = (TileEntityFusionKlystron) tile; + GL11.glTranslated(-1, 0, 0); GL11.glShadeModel(GL11.GL_SMOOTH); @@ -35,7 +38,7 @@ public class RenderFusionKlystron extends TileEntitySpecialRenderer implements I ResourceManager.fusion_klystron.renderPart("Klystron"); GL11.glPushMatrix(); - double rot = (System.currentTimeMillis() / 10) % 360D; + float rot = klystron.prevFan + (klystron.fan - klystron.prevFan) * interp; GL11.glTranslated(0, 2.5, 0); GL11.glRotated(rot, 1, 0, 0); GL11.glTranslated(0, -2.5, 0); diff --git a/src/main/java/com/hbm/render/tileentity/RenderFusionTorus.java b/src/main/java/com/hbm/render/tileentity/RenderFusionTorus.java index 5a8cf6ef3..7479b616d 100644 --- a/src/main/java/com/hbm/render/tileentity/RenderFusionTorus.java +++ b/src/main/java/com/hbm/render/tileentity/RenderFusionTorus.java @@ -5,6 +5,7 @@ import org.lwjgl.opengl.GL11; import com.hbm.blocks.ModBlocks; import com.hbm.main.ResourceManager; import com.hbm.render.item.ItemRenderBase; +import com.hbm.tileentity.machine.fusion.TileEntityFusionTorus; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; import net.minecraft.item.Item; @@ -20,20 +21,22 @@ public class RenderFusionTorus extends TileEntitySpecialRenderer implements IIte GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_CULL_FACE); + TileEntityFusionTorus torus = (TileEntityFusionTorus) tile; + GL11.glShadeModel(GL11.GL_SMOOTH); bindTexture(ResourceManager.fusion_torus_tex); ResourceManager.fusion_torus.renderPart("Torus"); GL11.glPushMatrix(); - double rot = (tile.getWorldObj().getTotalWorldTime() % 360) + interp; - GL11.glRotated(rot * 10, 0, 1, 0); + float rot = torus.prevMagnet + (torus.magnet - torus.prevMagnet) * interp; + GL11.glRotatef(rot, 0, 1, 0); ResourceManager.fusion_torus.renderPart("Magnet"); GL11.glPopMatrix(); - ResourceManager.fusion_torus.renderPart("Bolts1"); - ResourceManager.fusion_torus.renderPart("Bolts2"); - ResourceManager.fusion_torus.renderPart("Bolts3"); - ResourceManager.fusion_torus.renderPart("Bolts4"); + if(torus.connections[0]) ResourceManager.fusion_torus.renderPart("Bolts2"); + if(torus.connections[1]) ResourceManager.fusion_torus.renderPart("Bolts4"); + if(torus.connections[2]) ResourceManager.fusion_torus.renderPart("Bolts3"); + if(torus.connections[3]) ResourceManager.fusion_torus.renderPart("Bolts1"); GL11.glShadeModel(GL11.GL_FLAT); diff --git a/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionKlystron.java b/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionKlystron.java index e64f2137c..51adde280 100644 --- a/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionKlystron.java +++ b/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionKlystron.java @@ -2,6 +2,14 @@ package com.hbm.tileentity.machine.fusion; import java.util.Map.Entry; +import com.hbm.interfaces.IControlReceiver; +import com.hbm.inventory.container.ContainerFusionKlystron; +import com.hbm.inventory.fluid.Fluids; +import com.hbm.inventory.fluid.tank.FluidTank; +import com.hbm.inventory.gui.GUIFusionKlystron; +import com.hbm.lib.Library; +import com.hbm.tileentity.IGUIProvider; +import com.hbm.tileentity.TileEntityMachineBase; import com.hbm.uninos.GenNode; import com.hbm.uninos.UniNodespace; import com.hbm.uninos.networkproviders.KlystronNetwork; @@ -9,22 +17,78 @@ import com.hbm.uninos.networkproviders.KlystronNetworkProvider; import com.hbm.util.fauxpointtwelve.BlockPos; import com.hbm.util.fauxpointtwelve.DirPos; +import api.hbm.energymk2.IEnergyReceiverMK2; +import api.hbm.fluidmk2.IFluidStandardReceiverMK2; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.tileentity.TileEntity; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; -public class TileEntityFusionKlystron extends TileEntity { +public class TileEntityFusionKlystron extends TileEntityMachineBase implements IEnergyReceiverMK2, IFluidStandardReceiverMK2, IControlReceiver, IGUIProvider { protected GenNode klystronNode; + public static final long MAX_OUTPUT = 100_000_000; + public static final int AIR_CONSUMPTION = 2_500; + public long outputTarget; + public long output; + public long power; + public long maxPower; + + public float fan; + public float prevFan; + public float fanSpeed; + public static final float FAN_ACCELERATION = 0.125F; + + public FluidTank compair; + + public TileEntityFusionKlystron() { + super(1); + + compair = new FluidTank(Fluids.AIR, AIR_CONSUMPTION * 60); + } + @Override + public String getName() { + return "container.fusionKlystron"; + } + @Override public void updateEntity() { - super.updateEntity(); if(!worldObj.isRemote) { + this.maxPower = Math.max(1_000_000L, this.outputTarget * 100L); + + this.power = Library.chargeTEFromItems(slots, 0, power, maxPower); + + for(DirPos pos : getConPos()) { + this.trySubscribe(worldObj, pos); + this.trySubscribe(compair.getTankType(), worldObj, pos); + } + + this.output = 0; + + double powerFactor = TileEntityFusionTorus.getSpeedScaled(maxPower, power); + double airFactor = TileEntityFusionTorus.getSpeedScaled(compair.getMaxFill(), compair.getFill()); + double factor = Math.min(powerFactor, airFactor); + + long powerReq = (long) Math.ceil(outputTarget * factor); + int airReq = (int) Math.ceil(AIR_CONSUMPTION * factor); + + if(outputTarget > 0 && power >= powerReq && compair.getFill() >= airReq) { + this.output = powerReq; + + this.power -= powerReq; + this.compair.setFill(this.compair.getFill() - airReq); + } + if(klystronNode == null || klystronNode.expired) { ForgeDirection dir = ForgeDirection.getOrientation(this.getBlockMetadata() - 10).getOpposite(); klystronNode = UniNodespace.getNode(worldObj, xCoord + dir.offsetX * 4, yCoord + 2, zCoord + dir.offsetZ * 4, KlystronNetworkProvider.THE_PROVIDER); @@ -36,19 +100,129 @@ public class TileEntityFusionKlystron extends TileEntity { UniNodespace.createNode(worldObj, klystronNode); } - - if(klystronNode.net != null) klystronNode.net.addProvider(this); } + if(klystronNode.net != null) klystronNode.net.addProvider(this); + if(klystronNode != null && klystronNode.net != null) { KlystronNetwork net = (KlystronNetwork) klystronNode.net; - if(!net.receiverEntries.isEmpty()) { - //worldObj.func_147480_a(xCoord, yCoord, zCoord, false); + for(Object o : net.receiverEntries.entrySet()) { + Entry e = (Entry) o; + if(e.getKey() instanceof TileEntityFusionTorus) { // replace this with an interface should we ever get more acceptors + TileEntityFusionTorus torus = (TileEntityFusionTorus) e.getKey(); + + if(torus.isLoaded() && !torus.isInvalid()) { // check against zombie network members + torus.klystronEnergy += this.output; + break; // we only do one anyway + } + } } } + + this.networkPackNT(100); + } else { + + double mult = TileEntityFusionTorus.getSpeedScaled(outputTarget, output); + if(this.output > 0) this.fanSpeed += FAN_ACCELERATION * mult; + else this.fanSpeed -= FAN_ACCELERATION; + + this.fanSpeed = MathHelper.clamp_float(this.fanSpeed, 0F, 5F * (float) mult); + + this.prevFan = this.fan; + this.fan += this.fanSpeed; + + if(this.fan >= 360F) { + this.fan -= 360F; + this.prevFan -= 360F; + } } } + + public DirPos[] getConPos() { + ForgeDirection dir = ForgeDirection.getOrientation(this.getBlockMetadata() - 10); + ForgeDirection rot = dir.getRotation(ForgeDirection.UP); + + return new DirPos[] { + new DirPos(xCoord + dir.offsetX * 4, yCoord + 2, zCoord + dir.offsetZ * 4, dir), + new DirPos(xCoord + rot.offsetX * 3, yCoord, zCoord + rot.offsetZ * 3, rot), + new DirPos(xCoord - rot.offsetX * 3, yCoord, zCoord - rot.offsetZ * 3, rot.getOpposite()) + }; + } + + @Override + public void invalidate() { + super.invalidate(); + + if(!worldObj.isRemote) { + if(this.klystronNode != null) UniNodespace.destroyNode(worldObj, klystronNode); + } + } + + @Override + public void serialize(ByteBuf buf) { + super.serialize(buf); + buf.writeLong(power); + buf.writeLong(maxPower); + buf.writeLong(outputTarget); + buf.writeLong(output); + this.compair.serialize(buf); + } + + @Override + public void deserialize(ByteBuf buf) { + super.deserialize(buf); + this.power = buf.readLong(); + this.maxPower = buf.readLong(); + this.outputTarget = buf.readLong(); + this.output = buf.readLong(); + this.compair.deserialize(buf); + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + + this.power = nbt.getLong("power"); + this.maxPower = nbt.getLong("maxPower"); + this.outputTarget = nbt.getLong("outputTarget"); + + this.compair.readFromNBT(nbt, "t"); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + + nbt.setLong("power", power); + nbt.setLong("maxPower", maxPower); + nbt.setLong("outputTarget", outputTarget); + + this.compair.writeToNBT(nbt, "t"); + } + + @Override + public boolean isItemValidForSlot(int slot, ItemStack stack) { + if(slot == 0) return true; // battery + return false; + } + + @Override public long getPower() { return power; } + @Override public void setPower(long power) { this.power = power; } + @Override public long getMaxPower() { return maxPower; } + + @Override public FluidTank[] getAllTanks() { return new FluidTank[] {compair}; } + @Override public FluidTank[] getReceivingTanks() { return new FluidTank[] {compair}; } + + @Override + public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) { + return new ContainerFusionKlystron(player.inventory, this); + } + + @Override + public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) { + return new GUIFusionKlystron(player.inventory, this); + } AxisAlignedBB bb = null; @@ -74,4 +248,19 @@ public class TileEntityFusionKlystron extends TileEntity { public double getMaxRenderDistanceSquared() { return 65536.0D; } + + @Override + public boolean hasPermission(EntityPlayer player) { + return player.getDistanceSq(xCoord + 0.5, yCoord + 2.5, zCoord + 0.5) < 20 * 20; + } + + @Override + public void receiveControl(NBTTagCompound data) { + + if(data.hasKey("amount")) { + this.outputTarget = data.getLong("amount"); + if(this.outputTarget < 0) this.outputTarget = 0; + if(this.outputTarget > MAX_OUTPUT) this.outputTarget = MAX_OUTPUT; + } + } } diff --git a/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionTorus.java b/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionTorus.java index 4e418698f..c40b39fec 100644 --- a/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionTorus.java +++ b/src/main/java/com/hbm/tileentity/machine/fusion/TileEntityFusionTorus.java @@ -5,6 +5,7 @@ import com.hbm.inventory.container.ContainerFusionTorus; import com.hbm.inventory.fluid.Fluids; import com.hbm.inventory.fluid.tank.FluidTank; import com.hbm.inventory.gui.GUIFusionTorus; +import com.hbm.inventory.recipes.FusionRecipe; import com.hbm.items.ModItems; import com.hbm.lib.Library; import com.hbm.module.machine.ModuleMachineFusion; @@ -15,6 +16,7 @@ import com.hbm.uninos.INetworkProvider; import com.hbm.uninos.UniNodespace; import com.hbm.uninos.networkproviders.KlystronNetworkProvider; import com.hbm.uninos.networkproviders.PlasmaNetworkProvider; +import com.hbm.util.BobMathUtil; import com.hbm.util.fauxpointtwelve.BlockPos; import com.hbm.util.fauxpointtwelve.DirPos; @@ -26,6 +28,7 @@ import net.minecraft.inventory.Container; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -38,12 +41,23 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP protected GenNode[] klystronNodes; protected GenNode[] plasmaNodes; + public boolean[] connections; + + public long klystronEnergy; + public long plasmaEnergy; + public double fuelConsumption; + + public float magnet; + public float prevMagnet; + public float magnetSpeed; + public static final float MAGNET_ACCELERATION = 0.25F; public TileEntityFusionTorus() { super(3); klystronNodes = new GenNode[4]; plasmaNodes = new GenNode[4]; + connections = new boolean[4]; this.tanks = new FluidTank[4]; @@ -65,7 +79,6 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP @Override public void updateEntity() { - super.updateEntity(); if(!worldObj.isRemote) { @@ -76,14 +89,83 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP if(klystronNodes[i].net != null) klystronNodes[i].net.addReceiver(this); if(plasmaNodes[i].net != null) plasmaNodes[i].net.addProvider(this); } + + this.temperature += this.temp_passive_heating; + if(this.temperature > KELVIN + 20) this.temperature = KELVIN + 20; + + if(this.temperature > this.temperature_target) { + int cyclesTemp = (int) Math.ceil((Math.min(this.temperature - temperature_target, temp_change_max)) / temp_change_per_mb); + int cyclesCool = coolantTanks[0].getFill(); + int cyclesHot = coolantTanks[1].getMaxFill() - coolantTanks[1].getFill(); + int cycles = BobMathUtil.min(cyclesTemp, cyclesCool, cyclesHot); + + coolantTanks[0].setFill(coolantTanks[0].getFill() - cycles); + coolantTanks[1].setFill(coolantTanks[1].getFill() + cycles); + this.temperature -= this.temp_change_per_mb * cycles; + } + + for(DirPos pos : getConPos()) { + + if(worldObj.getTotalWorldTime() % 20 == 0) { + this.trySubscribe(worldObj, pos); + this.trySubscribe(coolantTanks[0].getTankType(), worldObj, pos); + if(tanks[0].getTankType() != Fluids.NONE) this.trySubscribe(tanks[0].getTankType(), worldObj, pos); + if(tanks[1].getTankType() != Fluids.NONE) this.trySubscribe(tanks[1].getTankType(), worldObj, pos); + if(tanks[2].getTankType() != Fluids.NONE) this.trySubscribe(tanks[2].getTankType(), worldObj, pos); + } + + if(coolantTanks[1].getFill() > 0) this.tryProvide(coolantTanks[1], worldObj, pos); + if(tanks[3].getFill() > 0) this.tryProvide(tanks[3], worldObj, pos); + } this.power = Library.chargeTEFromItems(slots, 0, power, this.getMaxPower()); - this.fusionModule.update(1D, 1D, true, slots[1]); + for(int i = 0; i < 4; i++) { + connections[i] = false; + if(klystronNodes[i] != null && klystronNodes[i].hasValidNet() && !klystronNodes[i].net.providerEntries.isEmpty()) connections[i] = true; + if(!connections[i] && plasmaNodes[i] != null && plasmaNodes[i].hasValidNet() && !plasmaNodes[i].net.receiverEntries.isEmpty()) connections[i] = true; + } + + FusionRecipe recipe = (FusionRecipe) this.fusionModule.getRecipe(); + + double powerFactor = TileEntityFusionTorus.getSpeedScaled(this.getMaxPower(), power); + double fuel0Factor = recipe != null && recipe.inputFluid.length > 0 ? getSpeedScaled(tanks[0].getMaxFill(), tanks[0].getFill()) : 1D; + double fuel1Factor = recipe != null && recipe.inputFluid.length > 1 ? getSpeedScaled(tanks[1].getMaxFill(), tanks[1].getFill()) : 1D; + double fuel2Factor = recipe != null && recipe.inputFluid.length > 2 ? getSpeedScaled(tanks[2].getMaxFill(), tanks[2].getFill()) : 1D; + double klystronFactor = recipe != null ? getSpeedScaled(recipe.ignitionTemp, this.klystronEnergy) : 1D; + + double factor = BobMathUtil.min(powerFactor, fuel0Factor, fuel1Factor, fuel2Factor, klystronFactor); + + this.plasmaEnergy = 0; + this.fuelConsumption = 0; + this.fusionModule.preUpdate(factor, 0.5D); + this.fusionModule.update(1D, 1D, this.isCool(), slots[1]); this.didProcess = this.fusionModule.didProcess; if(this.fusionModule.markDirty) this.markDirty(); + if(didProcess && recipe != null) { + this.plasmaEnergy = (long) Math.ceil(recipe.outputTemp * factor); + this.fuelConsumption = factor; + } this.networkPackNT(150); + + this.klystronEnergy = 0; + + } else { + + double powerFactor = TileEntityFusionTorus.getSpeedScaled(this.getMaxPower(), power); + if(this.didProcess) this.magnetSpeed += MAGNET_ACCELERATION; + else this.magnetSpeed -= MAGNET_ACCELERATION; + + this.magnetSpeed = MathHelper.clamp_float(this.magnetSpeed, 0F, 30F * (float) powerFactor); + + this.prevMagnet = this.magnet; + this.magnet += this.magnetSpeed; + + if(this.magnet >= 360F) { + this.magnet -= 360F; + this.prevMagnet -= 360F; + } } } @@ -100,27 +182,47 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP return node; } + @Override + public void invalidate() { + super.invalidate(); + + if(!worldObj.isRemote) { + for(GenNode node : klystronNodes) if(node != null) UniNodespace.destroyNode(worldObj, node); + for(GenNode node : plasmaNodes) if(node != null) UniNodespace.destroyNode(worldObj, node); + } + } + @Override public void serialize(ByteBuf buf) { super.serialize(buf); buf.writeBoolean(this.didProcess); + buf.writeLong(this.klystronEnergy); + buf.writeLong(this.plasmaEnergy); + buf.writeDouble(this.fuelConsumption); + this.fusionModule.serialize(buf); for(int i = 0; i < 4; i++) this.tanks[i].serialize(buf); + for(int i = 0; i < 4; i++) buf.writeBoolean(connections[i]); } @Override public void deserialize(ByteBuf buf) { super.deserialize(buf); this.didProcess = buf.readBoolean(); + this.klystronEnergy = buf.readLong(); + this.plasmaEnergy = buf.readLong(); + this.fuelConsumption = buf.readDouble(); + this.fusionModule.deserialize(buf); for(int i = 0; i < 4; i++) this.tanks[i].deserialize(buf); + for(int i = 0; i < 4; i++) connections[i] = buf.readBoolean(); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - for(int i = 0; i < 4; i++) this.tanks[i].readFromNBT(nbt, "t" + i); + for(int i = 0; i < 4; i++) this.tanks[i].readFromNBT(nbt, "ft" + i); this.power = nbt.getLong("power"); this.fusionModule.readFromNBT(nbt); @@ -130,7 +232,7 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - for(int i = 0; i < 4; i++) this.tanks[i].writeToNBT(nbt, "t" + i); + for(int i = 0; i < 4; i++) this.tanks[i].writeToNBT(nbt, "ft" + i); nbt.setLong("power", power); this.fusionModule.writeToNBT(nbt); @@ -140,16 +242,52 @@ public class TileEntityFusionTorus extends TileEntityCooledBase implements IGUIP public long getMaxPower() { return 10_000_000; } + + @Override public FluidTank[] getSendingTanks() { return new FluidTank[] {coolantTanks[1], tanks[3]}; } + @Override public FluidTank[] getReceivingTanks() { return new FluidTank[] {coolantTanks[0], tanks[0], tanks[1], tanks[2]}; } + @Override public FluidTank[] getAllTanks() { return new FluidTank[] {coolantTanks[0], coolantTanks[1], tanks[0], tanks[1], tanks[2], tanks[3]}; } /** Linearly scales up from 0% to 100% from 0 to 0.5, then stays at 100% */ public static double getSpeedScaled(double max, double level) { + if(max == 0) return 0D; if(level >= max * 0.5) return 1D; return level / max * 2D; } @Override public DirPos[] getConPos() { - return new DirPos[0]; // TBI + return new DirPos[] { + new DirPos(xCoord, yCoord - 1, zCoord, Library.NEG_Y), + new DirPos(xCoord, yCoord + 5, zCoord, Library.POS_Y), + + new DirPos(xCoord + 6, yCoord - 1, zCoord, Library.NEG_Y), + new DirPos(xCoord + 6, yCoord + 5, zCoord, Library.POS_Y), + new DirPos(xCoord + 6, yCoord - 1, zCoord + 2, Library.NEG_Y), + new DirPos(xCoord + 6, yCoord + 5, zCoord + 2, Library.POS_Y), + new DirPos(xCoord + 6, yCoord - 1, zCoord - 2, Library.NEG_Y), + new DirPos(xCoord + 6, yCoord + 5, zCoord - 2, Library.POS_Y), + + new DirPos(xCoord - 6, yCoord - 1, zCoord, Library.NEG_Y), + new DirPos(xCoord - 6, yCoord + 5, zCoord, Library.POS_Y), + new DirPos(xCoord - 6, yCoord - 1, zCoord + 2, Library.NEG_Y), + new DirPos(xCoord - 6, yCoord + 5, zCoord + 2, Library.POS_Y), + new DirPos(xCoord - 6, yCoord - 1, zCoord - 2, Library.NEG_Y), + new DirPos(xCoord - 6, yCoord + 5, zCoord - 2, Library.POS_Y), + + new DirPos(xCoord, yCoord - 1, zCoord + 6, Library.NEG_Y), + new DirPos(xCoord, yCoord + 5, zCoord + 6, Library.POS_Y), + new DirPos(xCoord + 2, yCoord - 1, zCoord + 6, Library.NEG_Y), + new DirPos(xCoord + 2, yCoord + 5, zCoord + 6, Library.POS_Y), + new DirPos(xCoord - 2, yCoord - 1, zCoord + 6, Library.NEG_Y), + new DirPos(xCoord - 2, yCoord + 5, zCoord + 6, Library.POS_Y), + + new DirPos(xCoord, yCoord - 1, zCoord - 6, Library.NEG_Y), + new DirPos(xCoord, yCoord + 5, zCoord - 6, Library.POS_Y), + new DirPos(xCoord + 2, yCoord - 1, zCoord - 6, Library.NEG_Y), + new DirPos(xCoord + 2, yCoord + 5, zCoord - 6, Library.POS_Y), + new DirPos(xCoord - 2, yCoord - 1, zCoord - 6, Library.NEG_Y), + new DirPos(xCoord - 2, yCoord + 5, zCoord - 6, Library.POS_Y), + }; } @Override diff --git a/src/main/java/com/hbm/uninos/UniNodespace.java b/src/main/java/com/hbm/uninos/UniNodespace.java index e9aaebb71..35daa8856 100644 --- a/src/main/java/com/hbm/uninos/UniNodespace.java +++ b/src/main/java/com/hbm/uninos/UniNodespace.java @@ -48,6 +48,12 @@ public class UniNodespace { } } + public static void destroyNode(World world, GenNode node) { + if(node != null) { + worlds.get(world).popNode(node); + } + } + public static void updateNodespace() { for(World world : MinecraftServer.getServer().worldServers) { diff --git a/src/main/resources/assets/hbm/textures/gui/reactors/gui_fusion_klystron.png b/src/main/resources/assets/hbm/textures/gui/reactors/gui_fusion_klystron.png new file mode 100644 index 0000000000000000000000000000000000000000..ec493582831835ef805a5b6267a15a3e2a6e51f9 GIT binary patch literal 3386 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