1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-19 19:25:48 +00:00

Added Industrial Miner

This commit is contained in:
TheBusyBiscuit 2020-06-05 00:00:07 +02:00
parent 4664fb00fe
commit c8f78379a1
7 changed files with 563 additions and 9 deletions

View File

@ -25,6 +25,7 @@
* Added Bone Blocks recipe to the Electric Press
* Added thai translations
* Dried Kelp Blocks can now be used in the Coal Generator
* Added Industrial Miner
#### Changes
* Fixed a few memory leaks

View File

@ -122,6 +122,16 @@ public class MultiBlock {
}
}
// This ensures that the Industrial Miner is still recognized while operating
if (a == Material.PISTON) {
return a == b || b == Material.MOVING_PISTON;
}
// This ensures that the Industrial Miner is still recognized while operating
if (b == Material.PISTON) {
return a == b || a == Material.MOVING_PISTON;
}
if (b != a) {
return false;
}

View File

@ -0,0 +1,198 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.multiblocks.MultiBlockMachine;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link IndustrialMiner} is a {@link MultiBlockMachine} that can mine any
* ores it finds in a given range underneath where it was placed.
*
* <i>And for those of you who are wondering... yes this is the replacement for the
* long-time deprecated Digital Miner.</i>
*
* @author TheBusyBiscuit
*
*/
public class IndustrialMiner extends MultiBlockMachine {
protected final Map<Location, IndustrialMinerInstance> activeMiners = new HashMap<>();
protected final List<MachineFuel> fuelTypes = new ArrayList<>();
private final int range;
private final boolean silkTouch;
public IndustrialMiner(Category category, SlimefunItemStack item, Material baseMaterial, boolean silkTouch, int range) {
super(category, item, new ItemStack[] { null, null, null, new CustomItem(Material.PISTON, "Piston (facing up)"), new ItemStack(Material.CHEST), new CustomItem(Material.PISTON, "Piston (facing up)"), new ItemStack(baseMaterial), new ItemStack(SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14) ? Material.BLAST_FURNACE : Material.FURNACE), new ItemStack(baseMaterial) }, new ItemStack[0], BlockFace.UP);
this.range = range;
this.silkTouch = silkTouch;
registerDefaultFuelTypes();
}
/**
* This returns whether this {@link IndustrialMiner} will output ores as they are.
* Similar to the Silk Touch {@link Enchantment}.
*
* @return Whether to treat ores with Silk Touch
*/
public boolean hasSilkTouch() {
return silkTouch;
}
/**
* This method returns the range of the {@link IndustrialMiner}.
* The total area will be determined by the range multiplied by 2 plus the actual center
* of the machine.
*
* So a range of 3 will make the {@link IndustrialMiner} affect an area of 7x7 blocks.
* 3 on all axis, plus the center of the machine itself.
*
* @return The range of this {@link IndustrialMiner}
*/
public int getRange() {
return range;
}
/**
* This registers the various types of fuel that can be used in the
* {@link IndustrialMiner}.
*/
protected void registerDefaultFuelTypes() {
// Coal & Charcoal
fuelTypes.add(new MachineFuel(4, new ItemStack(Material.COAL)));
fuelTypes.add(new MachineFuel(4, new ItemStack(Material.CHARCOAL)));
fuelTypes.add(new MachineFuel(40, new ItemStack(Material.COAL_BLOCK)));
fuelTypes.add(new MachineFuel(10, new ItemStack(Material.DRIED_KELP_BLOCK)));
fuelTypes.add(new MachineFuel(4, new ItemStack(Material.BLAZE_ROD)));
// Logs
for (Material mat : Tag.LOGS.getValues()) {
fuelTypes.add(new MachineFuel(1, new ItemStack(mat)));
}
}
/**
* This method returns the outcome that mining certain ores yields.
*
* @param ore
* The {@link Material} of the ore that was mined
*
* @return The outcome when mining this ore
*/
public ItemStack getOutcome(Material ore) {
if (hasSilkTouch()) {
return new ItemStack(ore);
}
Random random = ThreadLocalRandom.current();
switch (ore) {
case COAL_ORE:
return new ItemStack(Material.COAL);
case DIAMOND_ORE:
return new ItemStack(Material.DIAMOND);
case EMERALD_ORE:
return new ItemStack(Material.EMERALD);
case NETHER_QUARTZ_ORE:
return new ItemStack(Material.QUARTZ);
case REDSTONE_ORE:
return new ItemStack(Material.REDSTONE, 4 + random.nextInt(2));
case LAPIS_ORE:
return new ItemStack(Material.LAPIS_LAZULI, 4 + random.nextInt(4));
default:
// This includes Iron and Gold ore
return new ItemStack(ore);
}
}
/**
* This registers a new fuel type for this {@link IndustrialMiner}.
*
* @param ores
* The amount of ores this allows you to mine
* @param item
* The item that shall be consumed
*/
public void addFuelType(int ores, ItemStack item) {
Validate.isTrue(ores > 1 && ores % 2 == 0, "The amount of ores must be at least 2 and a multiple of 2.");
fuelTypes.add(new MachineFuel(ores / 2, item));
}
@Override
public String getLabelLocalPath() {
return "guide.tooltips.recipes.generator";
}
@Override
public List<ItemStack> getDisplayRecipes() {
List<ItemStack> list = new ArrayList<>();
for (MachineFuel fuel : fuelTypes) {
ItemStack item = fuel.getInput().clone();
ItemMeta im = item.getItemMeta();
List<String> lore = new ArrayList<>();
lore.add(ChatColors.color("&8\u21E8 &7Lasts for max. " + fuel.getTicks() + " Ores"));
im.setLore(lore);
item.setItemMeta(im);
list.add(item);
}
return list;
}
@Override
public void onInteract(Player p, Block b) {
if (activeMiners.containsKey(b.getLocation())) {
SlimefunPlugin.getLocal().sendMessage(p, "machines.INDUSTRIAL_MINER.already-running");
return;
}
Block chest = b.getRelative(BlockFace.UP);
Block[] pistons = findPistons(chest);
int mod = getRange();
Location start = b.getLocation().clone().add(-mod, -1, -mod);
Location end = b.getLocation().clone().add(mod, -1, mod);
IndustrialMinerInstance instance = new IndustrialMinerInstance(this, p.getUniqueId(), chest, pistons, start, end);
instance.start(b);
}
private Block[] findPistons(Block chest) {
Block northern = chest.getRelative(BlockFace.NORTH);
if (northern.getType() == Material.PISTON) {
return new Block[] { northern, chest.getRelative(BlockFace.SOUTH) };
}
else {
return new Block[] { chest.getRelative(BlockFace.WEST), chest.getRelative(BlockFace.EAST) };
}
}
}

View File

@ -0,0 +1,333 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Chest;
import org.bukkit.block.data.type.Piston;
import org.bukkit.block.data.type.PistonHead;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.cscorelib2.scheduling.TaskQueue;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel;
import me.mrCookieSlime.Slimefun.api.Slimefun;
class IndustrialMinerInstance implements Runnable {
private final IndustrialMiner miner;
private final UUID owner;
private int fuel = 0;
private int ores = 0;
private boolean running = false;
private final Block chest;
private final Block[] pistons;
private final Location start;
private final Location end;
private final int height;
private int x;
private int z;
public IndustrialMinerInstance(IndustrialMiner miner, UUID owner, Block chest, Block[] pistons, Location start, Location end) {
this.miner = miner;
this.owner = owner;
this.chest = chest;
this.pistons = pistons;
this.start = start;
this.end = end;
this.height = start.getBlockY();
this.x = start.getBlockX();
this.z = start.getBlockZ();
}
/**
* This starts the {@link IndustrialMiner} at the given {@link Block}.
*
* @param b
* The {@link Block} which marks the center of this {@link IndustrialMiner}
*/
public void start(Block b) {
miner.activeMiners.put(b.getLocation(), this);
running = true;
warmUp();
}
/**
* This method stops the {@link IndustrialMiner}.
*/
public void stop() {
running = false;
miner.activeMiners.remove(chest.getRelative(BlockFace.DOWN).getLocation());
}
/**
* This method stops the {@link IndustrialMiner} with an error message.
* The error message is a path to the location in Slimefun's localization files.
*
* @param error
* The error message to send
*/
public void stop(String error) {
Player p = Bukkit.getPlayer(owner);
if (p != null) {
SlimefunPlugin.getLocal().sendMessage(p, error);
}
stop();
}
/**
* This method starts the warm-up animation for the {@link IndustrialMiner}.
*/
private void warmUp() {
fuel = consumeFuel();
if (fuel <= 0) {
// This Miner has not enough fuel.
stop("machines.INDUSTRIAL_MINER.no-fuel");
return;
}
TaskQueue queue = new TaskQueue();
queue.thenRun(4, () -> setPistonState(pistons[0], true));
queue.thenRun(10, () -> setPistonState(pistons[0], false));
queue.thenRun(8, () -> setPistonState(pistons[1], true));
queue.thenRun(10, () -> setPistonState(pistons[1], false));
queue.thenRun(6, () -> setPistonState(pistons[0], true));
queue.thenRun(9, () -> setPistonState(pistons[0], false));
queue.thenRun(4, () -> setPistonState(pistons[1], true));
queue.thenRun(7, () -> setPistonState(pistons[1], false));
queue.thenRun(3, () -> setPistonState(pistons[0], true));
queue.thenRun(4, () -> setPistonState(pistons[0], false));
queue.thenRun(2, () -> setPistonState(pistons[1], true));
queue.thenRun(3, () -> setPistonState(pistons[1], false));
queue.thenRun(1, () -> setPistonState(pistons[0], true));
queue.thenRun(3, () -> setPistonState(pistons[0], false));
queue.thenRun(1, () -> setPistonState(pistons[1], true));
queue.thenRun(3, () -> setPistonState(pistons[1], false));
queue.thenRun(1, this);
queue.execute(SlimefunPlugin.instance);
}
@Override
public void run() {
if (!running) {
return;
}
try {
TaskQueue queue = new TaskQueue();
queue.thenRun(1, () -> setPistonState(pistons[0], true));
queue.thenRun(3, () -> setPistonState(pistons[0], false));
queue.thenRun(1, () -> setPistonState(pistons[1], true));
queue.thenRun(3, () -> setPistonState(pistons[1], false));
queue.thenRun(() -> {
Block furnace = chest.getRelative(BlockFace.DOWN);
furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, Material.STONE);
for (int y = height; y > 0; y--) {
Block b = start.getWorld().getBlockAt(x, y, z);
if (!SlimefunPlugin.getProtectionManager().hasPermission(Bukkit.getOfflinePlayer(owner), b, ProtectableAction.BREAK_BLOCK)) {
stop("machines.INDUSTRIAL_MINER.no-permission");
return;
}
if (b.getType().name().endsWith("_ORE") && push(miner.getOutcome(b.getType()))) {
furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, b.getType());
furnace.getWorld().playSound(furnace.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 0.2F, 1F);
b.setType(Material.AIR);
fuel--;
ores++;
// Repeat the same column when we hit an ore.
Slimefun.runSync(this, 5);
return;
}
}
nextColumn();
});
queue.execute(SlimefunPlugin.instance);
}
catch (Exception e) {
Slimefun.getLogger().log(Level.SEVERE, "An Error occured while running an Industrial Miner", e);
stop();
}
}
/**
* This advanced the {@link IndustrialMiner} to the next column
*/
private void nextColumn() {
if (x < end.getBlockX()) {
x++;
}
else if (z < end.getBlockZ()) {
x = start.getBlockX();
z++;
}
else {
stop();
Player p = Bukkit.getPlayer(owner);
if (p != null) {
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 0.4F, 1F);
SlimefunPlugin.getLocal().sendMessage(p, "machines.INDUSTRIAL_MINER.finished", msg -> msg.replace("%ores%", String.valueOf(ores)));
}
return;
}
Slimefun.runSync(this, 5);
}
private boolean push(ItemStack outcome) {
if (fuel < 1) {
fuel = consumeFuel();
}
if (fuel > 0) {
if (chest.getType() == Material.CHEST) {
Inventory inv = ((Chest) chest.getState()).getBlockInventory();
if (InvUtils.fits(inv, outcome)) {
inv.addItem(outcome);
return true;
}
else {
stop("machines.INDUSTRIAL_MINER.chest-full");
}
}
else {
// The chest has been destroyed
stop("machines.INDUSTRIAL_MINER.destroyed");
}
}
else {
stop("machines.INDUSTRIAL_MINER.no-fuel");
}
return false;
}
/**
* This consumes fuel from the given {@link Chest}.
*
* @return The gained fuel value
*/
private int consumeFuel() {
if (chest.getType() == Material.CHEST) {
Inventory inv = ((Chest) chest.getState()).getBlockInventory();
for (int i = 0; i < inv.getSize(); i++) {
for (MachineFuel fuelType : miner.fuelTypes) {
ItemStack item = inv.getContents()[i];
if (fuelType.test(item)) {
ItemUtils.consumeItem(item, false);
return fuelType.getTicks();
}
}
}
}
return 0;
}
private void setPistonState(Block block, boolean extended) {
if (!running) {
return;
}
try {
// Smoke Particles around the Chest for dramatic effect
Location particleLoc = chest.getLocation().clone().add(0, -1, 0);
block.getWorld().spawnParticle(Particle.SMOKE_NORMAL, particleLoc, 16, 1, 1, 1, 0);
if (block.getType() == Material.MOVING_PISTON) {
// Yeah it isn't really cool when this happens
block.getRelative(BlockFace.UP).setType(Material.AIR);
}
else if (block.getType() == Material.PISTON) {
Block above = block.getRelative(BlockFace.UP);
if (above.isEmpty() || above.getType() == Material.PISTON_HEAD) {
Piston piston = (Piston) block.getBlockData();
if (piston.getFacing() == BlockFace.UP) {
piston.setExtended(extended);
block.setBlockData(piston, false);
// Updating the Piston Head
if (extended) {
PistonHead head = (PistonHead) Material.PISTON_HEAD.createBlockData();
head.setFacing(BlockFace.UP);
block.getRelative(BlockFace.UP).setBlockData(head, false);
}
else {
block.getRelative(BlockFace.UP).setType(Material.AIR);
}
block.getWorld().playSound(block.getLocation(), extended ? Sound.BLOCK_PISTON_EXTEND : Sound.BLOCK_PISTON_CONTRACT, 0.2F, 1F);
}
else {
// The pistons must be facing upwards
stop("machines.INDUSTRIAL_MINER.piston-facing");
}
}
else {
// The pistons must be facing upwards
stop("machines.INDUSTRIAL_MINER.piston-space");
}
}
else {
// The piston has been destroyed
stop("machines.INDUSTRIAL_MINER.destroyed");
}
}
catch (Exception e) {
Slimefun.getLogger().log(Level.SEVERE, "An Error occured while moving a Piston for an Industrial Miner", e);
stop();
}
}
}

View File

@ -135,6 +135,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Autom
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Compressor;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.GrindStone;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.IndustrialMiner;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Juicer;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.MagicWorkbench;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.MakeshiftSmeltery;
@ -1021,6 +1022,8 @@ public final class SlimefunItemSetup {
.register(plugin);
new AutomatedPanningMachine(categories.basicMachines).register(plugin);
new IndustrialMiner(categories.basicMachines, SlimefunItems.INDUSTRIAL_MINER, Material.IRON_BLOCK, false, 3).register(plugin);
new SlimefunItem(categories.magicalArmor, SlimefunItems.BOOTS_OF_THE_STOMPER, RecipeType.ARMOR_FORGE,
new ItemStack[] {null, null, null, new ItemStack(Material.YELLOW_WOOL), null, new ItemStack(Material.YELLOW_WOOL), new ItemStack(Material.PISTON), null, new ItemStack(Material.PISTON)})

View File

@ -16,11 +16,11 @@ commands:
reset-target: '&cYour Knowledge has been reset'
backpack:
description: Retrieve an existing backpack
invalid-id: '&4The backpack id must be a non-negative number!'
player-never-joined: '&4No player with that name has ever joined the server!'
backpack-does-not-exist: '&4That backpack does not exist!'
restored-backpack-given: '&bBackpack restored successfully! Added to your inventory!'
description: Retrieve a copy of an existing backpack
invalid-id: '&4The id must be a non-negative number!'
player-never-joined: '&4No player with that name could be found!'
backpack-does-not-exist: '&4The specified backpack does not exist!'
restored-backpack-given: '&aYour backpack has been restored and was added to your inventory!'
guide:
locked: 'LOCKED'
@ -202,7 +202,17 @@ machines:
CARGO_NODES:
must-be-placed: '&4Must be placed onto a chest or machine!'
INDUSTRIAL_MINER:
no-fuel: '&cYour Industrial Miner ran out of fuel! Put your fuel into the chest above.'
piston-facing: '&cYour Industrial Miner requires pistons to face upwards!'
piston-space: '&cThe two pistons need to have an empty block above them!'
destroyed: '&cYour Industrial Miner seems to have been destroyed.'
already-running: '&cThis Industrial Miner is already running!'
full-chest: '&cThe Chest of your Industrial Miner is full!'
no-permission: '&4You do not seem to have permission to operate an Industrial Miner here!'
finished: '&eYour Industrial Miner has finished! It obtained a total of %ores% ore(s)!'
anvil:
not-working: '&4You cannot use Slimefun Items in an anvil!'
@ -210,9 +220,6 @@ backpack:
already-open: '&cSorry, this Backpack is open somewhere else!'
no-stack: '&cYou cannot stack Backpacks'
miner:
no-ores: '&eSorry, I could not find any Ores nearby!'
workbench:
not-enhanced: '&4You cannot use Slimefun Items in a normal workbench'

View File

@ -231,3 +231,5 @@ slimefun:
kelp_cookie: Tasty Kelp
makeshift_smeltery: Improvised Smeltery
tree_growth_accelerator: Faster Trees
industrial_miner: Industrial Mining
advanced_industrial_miner: Better Mining