Skip to content

Instantly share code, notes, and snippets.

@SpaceManiac
Created October 20, 2014 06:07
Show Gist options
  • Save SpaceManiac/d3333d8a8dfb73ce7c95 to your computer and use it in GitHub Desktop.
Save SpaceManiac/d3333d8a8dfb73ce7c95 to your computer and use it in GitHub Desktop.
Double-speed furnace implementation, comments note how to make normal-speed.
package com.platymuus.bukkit.endmachines.machines;
import com.platymuus.bukkit.endmachines.MachineDescription;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.*;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Furnace which acts at double the normal speed.
*/
public class FurnaceUpgrade extends Machine {
public static final MachineDescription DESCRIPTION = new MachineDescription("furnace", "End Furnace", FurnaceUpgrade.class)
.shape("S/S", "SxS", "nnn")
.ingredient('S', Material.ENDER_STONE)
.ingredient('/', Material.BLAZE_ROD)
.ingredient('x', Material.PAPER)
.ingredient('n', Material.NETHERRACK);
private static final int INPUT = 0;
private static final int FUEL = 1;
private static final int OUTPUT = 2;
private final FurnaceListener listener = new FurnaceListener();
private final SmeltTask task = new SmeltTask();
private final Inventory inventory = Bukkit.getServer().createInventory(null, InventoryType.FURNACE, "End Furnace");
private int cookTime = 0; // Up to 200, in ticks
private int burnTime = 0; // Remaining burn ticks, out of fuelTicks
private int fuelTicks = 0; // Max fuel ticks
private int flameTime = 0; // For vfx only
@Override
public boolean construct(PlayerInteractEvent event) {
return matchesType(event, Material.FURNACE) || matchesType(event, Material.BURNING_FURNACE);
}
@Override
public void destroy() {
keepInventoryEmpty(inventory);
if (getBlock().getState() instanceof InventoryHolder) {
Inventory inv = ((InventoryHolder) getBlock().getState()).getInventory();
inv.setContents(inventory.getContents());
inventory.clear();
} else {
// you messed it up so no items back for you
}
}
@Override
public void activate() {
register(listener);
task.runTaskTimer(getPlugin(), 0, 1);
}
@Override
public void deactivate() {
unregister(listener);
task.cancel();
listener.close();
}
@Override
public void loadData(ConfigurationSection data) {
for (int i = 0; i < inventory.getSize(); ++i) {
inventory.setItem(i, data.getItemStack("item" + i));
}
cookTime = data.getInt("cookTime", 0);
burnTime = data.getInt("burnTime", 0);
fuelTicks = data.getInt("fuelTicks", 0);
}
@Override
public void saveData(ConfigurationSection data) {
for (int i = 0; i < inventory.getSize(); ++i) {
data.set("item" + i, inventory.getItem(i));
}
data.set("cookTime", cookTime);
data.set("burnTime", burnTime);
data.set("fuelTicks", fuelTicks);
}
private class FurnaceListener implements Listener {
private final Set<InventoryView> openViews = new HashSet<InventoryView>();
@EventHandler(ignoreCancelled = true)
public void handleInteract(PlayerInteractEvent event) {
if (shouldInteract(event)) {
openViews.add(event.getPlayer().openInventory(inventory));
event.setCancelled(true);
update();
}
}
@EventHandler
public void handleClick(InventoryClickEvent event) {
if (!openViews.contains(event.getView())) {
return;
}
if (event.isShiftClick()) {
// There's a nasty crash bug with shift-clicking atm so let's avoid it.
if (event.getWhoClicked() instanceof Player) {
((Player) event.getWhoClicked()).sendMessage(PREFIX + ChatColor.RED + "Shift-clicking in End Furnaces is broken");
}
event.setCancelled(true);
return;
}
if (event.getRawSlot() == OUTPUT && event.getCursor() != null && event.getCursor().getType() != Material.AIR) {
// Only allow taking things /out/ of the result slot
event.setCancelled(true);
ItemStack output = inventory.getItem(OUTPUT), cursor = event.getCursor();
if (output != null && canStack(output, event.getCursor())) {
int toAdd = Math.min(cursor.getType().getMaxStackSize() - cursor.getAmount(), output.getAmount());
cursor.setAmount(cursor.getAmount() + toAdd);
output.setAmount(output.getAmount() - toAdd);
if (output.getAmount() <= 0) {
inventory.setItem(OUTPUT, null);
}
}
}
}
@EventHandler
public void handleClose(InventoryCloseEvent event) {
if (openViews.contains(event.getView())) {
openViews.remove(event.getView());
}
}
@EventHandler(ignoreCancelled = true)
public void handleHopper(InventoryMoveItemEvent event) {
// this doesn't work yet.
// the hopper has to be trying to pull items in the first place,
// which it won't be if we keep it empty as we do currently
if (!(getBlock().getState() instanceof InventoryHolder)) return;
Inventory realInv = ((InventoryHolder) getBlock().getState()).getInventory();
if (event.getSource() == realInv) {
event.setCancelled(true);
// move one item from our result slot to the target
ItemStack item = inventory.getItem(OUTPUT).clone();
item.setAmount(item.getAmount() - 1);
inventory.setItem(OUTPUT, item.getAmount() > 0 ? item.clone() : null);
item.setAmount(1);
addItems(event.getDestination(), item);
}
}
public void close() {
for (InventoryView v : openViews) {
v.close();
}
openViews.clear();
}
public void update() {
for (InventoryView v : openViews) {
v.setProperty(InventoryView.Property.COOK_TIME, cookTime);
v.setProperty(InventoryView.Property.BURN_TIME, burnTime);
v.setProperty(InventoryView.Property.TICKS_FOR_CURRENT_FUEL, fuelTicks);
}
}
}
private class SmeltTask extends BukkitRunnable {
private ItemStack output;
private Material cachedInput;
public void run() {
// this is basically the furnace code
keepInventoryEmpty(inventory);
// make some flaming effects
if (++flameTime >= 30) {
Location loc = getLocation();
loc.getWorld().playEffect(loc.clone().add(0.5, 0.5, 0.5), Effect.MOBSPAWNER_FLAMES, 0);
flameTime = 0;
}
// if there's nothing at all to burn, just sit there, for efficiency
if (inventory.getItem(FUEL) == null && burnTime == 0) return;
// nothing wll make burnTime forcibly zero
// cookTime will forcibly be 0 if burnTime is 0 or input and output are incompatible
// cache the input->output mapping so we don't have to constantly search for it
ItemStack input = inventory.getItem(INPUT);
if (cachedInput != (input == null ? null : input.getType())) {
cachedInput = (input == null ? null : input.getType());
output = findOutput(inventory.getItem(INPUT));
}
boolean goodOutput = output != null && canStackAmount(output, inventory.getItem(OUTPUT));
// run down the burn timer
burnTime -= 1;
if (burnTime <= 0) {
if (goodOutput) {
// add to burn time - 10 would be 20 if the furnace was normal speed
burnTime = fuelTicks = (int)(10 * findFuel(inventory.getItem(FUEL)));
if (burnTime > 0) {
// consume fuel
int amt = inventory.getItem(FUEL).getAmount();
if (amt <= 1) {
inventory.setItem(FUEL, null);
} else {
inventory.getItem(FUEL).setAmount(amt - 1);
}
}
} else {
burnTime = 0;
}
}
byte data = getBlock().getData();
if (burnTime == 0 || !goodOutput) {
// no fuel burning or invalid output
cookTime = 0;
// make furnace off
getBlock().setType(Material.FURNACE);
} else {
cookTime += 2; // On normal furnace, this would be a 1
if (cookTime >= 200) {
cookTime = 0;
// produce output
if (inventory.getItem(OUTPUT) == null) {
inventory.setItem(OUTPUT, output);
} else {
inventory.getItem(OUTPUT).setAmount(inventory.getItem(OUTPUT).getAmount() + output.getAmount());
}
// consume input
int amt = inventory.getItem(INPUT).getAmount();
if (amt <= 1) {
inventory.setItem(INPUT, null);
} else {
inventory.getItem(INPUT).setAmount(amt - 1);
}
// play a little particle effect
getLocation().getWorld().playEffect(getLocation(), Effect.ENDER_SIGNAL, 0);
}
// make furnace on
getBlock().setType(Material.BURNING_FURNACE);
}
getBlock().setData(data);
listener.update();
}
private double findFuel(ItemStack fuel) {
if (fuel == null) return 0;
switch (fuel.getType()) {
case LAVA_BUCKET:
return 1000;
case BLAZE_ROD:
return 120;
case COAL:
return 80;
case LOG:
case LOG_2:
case WOOD:
case WOOD_PLATE:
case FENCE:
case FENCE_GATE:
case WOOD_STAIRS:
case TRAP_DOOR:
case WORKBENCH:
case BOOKSHELF:
case CHEST:
case JUKEBOX:
case NOTE_BLOCK:
case HUGE_MUSHROOM_1:
case HUGE_MUSHROOM_2:
return 15;
case WOOD_AXE:
case WOOD_SPADE:
case WOOD_PICKAXE:
case WOOD_HOE:
case WOOD_SWORD:
return 10;
case WOOD_STEP:
return 7.5;
case STICK:
case SAPLING:
return 5;
default:
return 0;
}
}
private ItemStack findOutput(ItemStack input) {
if (input == null || input.getType() == Material.AIR) return null;
Iterator<Recipe> iter = Bukkit.getServer().recipeIterator();
while (iter.hasNext()) {
Recipe r = iter.next();
if (r instanceof FurnaceRecipe) {
FurnaceRecipe fr = (FurnaceRecipe) r;
// Intentionally ignore durability values to align with CraftBukkit behaviour
if (fr.getInput().getType() == input.getType()) {
return fr.getResult();
}
}
}
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment