mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2025-06-27 11:14:44 +02:00
Introduction of new Alchemy skill!
This commit is contained in:
83
src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java
Normal file
83
src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java
Normal file
@ -0,0 +1,83 @@
|
||||
package com.gmail.nossr50.skills.alchemy;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.runnables.skills.AlchemyBrewTask;
|
||||
|
||||
public final class Alchemy {
|
||||
public enum Tier {
|
||||
EIGHT(8),
|
||||
SEVEN(7),
|
||||
SIX(6),
|
||||
FIVE(5),
|
||||
FOUR(4),
|
||||
THREE(3),
|
||||
TWO(2),
|
||||
ONE(1);
|
||||
|
||||
int numerical;
|
||||
|
||||
private Tier(int numerical) {
|
||||
this.numerical = numerical;
|
||||
}
|
||||
|
||||
public int toNumerical() {
|
||||
return numerical;
|
||||
}
|
||||
|
||||
public static Tier fromNumerical(int numerical) {
|
||||
for (Tier tier : Tier.values()) {
|
||||
if (tier.toNumerical() == numerical) {
|
||||
return tier;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected int getLevel() {
|
||||
return AdvancedConfig.getInstance().getConcoctionsTierLevel(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static int catalysisUnlockLevel = AdvancedConfig.getInstance().getCatalysisUnlockLevel();
|
||||
public static int catalysisMaxBonusLevel = AdvancedConfig.getInstance().getCatalysisMaxBonusLevel();
|
||||
public static double catalysisMinSpeed = AdvancedConfig.getInstance().getCatalysisMinSpeed();
|
||||
public static double catalysisMaxSpeed = AdvancedConfig.getInstance().getCatalysisMaxSpeed();
|
||||
|
||||
public static double potionXp = ExperienceConfig.getInstance().getPotionXP();
|
||||
|
||||
public static Map<Block, AlchemyBrewTask> brewingStandMap = new HashMap<Block, AlchemyBrewTask>();
|
||||
|
||||
private Alchemy() {};
|
||||
|
||||
/**
|
||||
* Calculate base brewing speed, given a skill level and ignoring all perks.
|
||||
*
|
||||
* @param skillLevel Skill level used for calculation.
|
||||
* @return Base brewing speed for the level.
|
||||
*/
|
||||
public static double calculateBrewSpeed(int skillLevel) {
|
||||
if (skillLevel < catalysisUnlockLevel) {
|
||||
return catalysisMinSpeed;
|
||||
}
|
||||
|
||||
return Math.min(catalysisMaxSpeed, catalysisMinSpeed + (catalysisMaxSpeed - catalysisMinSpeed) * (skillLevel - catalysisUnlockLevel) / (catalysisMaxBonusLevel - catalysisUnlockLevel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish all active brews. Used upon Disable to prevent vanilla potions from being brewed upon next Enable.
|
||||
*/
|
||||
public static void finishAllBrews() {
|
||||
mcMMO.p.getLogger().info("Completing " + brewingStandMap.size() + " unfinished Alchemy brews.");
|
||||
for (Entry<Block, AlchemyBrewTask> entry : brewingStandMap.entrySet()) {
|
||||
entry.getValue().finishImmediately();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.gmail.nossr50.skills.alchemy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.config.potion.PotionConfig;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.SkillType;
|
||||
import com.gmail.nossr50.skills.SkillManager;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
|
||||
public class AlchemyManager extends SkillManager {
|
||||
private final double LUCKY_MODIFIER = 4.0 / 3.0;
|
||||
|
||||
public AlchemyManager(McMMOPlayer mcMMOPlayer) {
|
||||
super(mcMMOPlayer, SkillType.ALCHEMY);
|
||||
}
|
||||
|
||||
public boolean canCatalysis() {
|
||||
return Permissions.catalysis(getPlayer());
|
||||
}
|
||||
|
||||
public boolean canUseIngredient(ItemStack item) {
|
||||
for (ItemStack ingredient : getIngredients()) {
|
||||
if (item.isSimilar(ingredient)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getTier() {
|
||||
for (Alchemy.Tier tier : Alchemy.Tier.values()) {
|
||||
if (getSkillLevel() >= tier.getLevel()) {
|
||||
return tier.toNumerical();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<ItemStack> getIngredients() {
|
||||
switch (Alchemy.Tier.fromNumerical(getTier())) {
|
||||
case EIGHT:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierEight;
|
||||
case SEVEN:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierSeven;
|
||||
case SIX:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierSix;
|
||||
case FIVE:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierFive;
|
||||
case FOUR:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierFour;
|
||||
case THREE:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierThree;
|
||||
case TWO:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierTwo;
|
||||
default:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierOne;
|
||||
}
|
||||
}
|
||||
|
||||
public String getIngredientList() {
|
||||
String list = "";
|
||||
for (ItemStack ingredient : getIngredients()) {
|
||||
list += ", " + StringUtils.getPrettyItemString(ingredient.getType()) + (ingredient.getDurability() != 0 ? ":" + ingredient.getDurability() : "");
|
||||
}
|
||||
return list.substring(2);
|
||||
}
|
||||
|
||||
public double getBrewSpeed() {
|
||||
return Alchemy.calculateBrewSpeed(getSkillLevel());
|
||||
}
|
||||
|
||||
public double getBrewSpeedLucky() {
|
||||
return LUCKY_MODIFIER * Alchemy.calculateBrewSpeed(getSkillLevel());
|
||||
}
|
||||
|
||||
public void handlePotionBrewSuccesses(int amount) {
|
||||
applyXpGain((float) (ExperienceConfig.getInstance().getPotionXP() * amount));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,349 @@
|
||||
package com.gmail.nossr50.skills.alchemy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BrewingStand;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
import org.bukkit.event.inventory.InventoryType.SlotType;
|
||||
import org.bukkit.inventory.BrewerInventory;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.config.potion.PotionConfig;
|
||||
import com.gmail.nossr50.datatypes.AlchemyPotion;
|
||||
import com.gmail.nossr50.runnables.PlayerUpdateInventoryTask;
|
||||
import com.gmail.nossr50.runnables.skills.AlchemyBrewCheckTask;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
|
||||
public final class AlchemyPotionBrewer {
|
||||
private final static int[] BOTTLE_SLOTS = new int[] { 0, 1, 2 };
|
||||
private final static int INGREDIENT_SLOT = 3;
|
||||
|
||||
public static boolean isValidBrew(Player player, ItemStack[] contents) {
|
||||
if (isValidIngredient(player, contents[INGREDIENT_SLOT])) {
|
||||
for (int bottle : BOTTLE_SLOTS) {
|
||||
if (contents[bottle] != null && contents[bottle].getType() == Material.POTION) {
|
||||
AlchemyPotion potion = PotionConfig.getInstance().potionMap.get(contents[bottle].getDurability());
|
||||
|
||||
if (getChildPotion(potion, contents[INGREDIENT_SLOT]) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static AlchemyPotion getChildPotion(AlchemyPotion potion, ItemStack ingredient) {
|
||||
if (potion != null && potion.getChildDataValue(ingredient) != -1) {
|
||||
return PotionConfig.getInstance().potionMap.get(potion.getChildDataValue(ingredient));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isEmpty(ItemStack item) {
|
||||
return item == null || item.getType() == Material.AIR || item.getAmount() == 0;
|
||||
}
|
||||
|
||||
private static boolean removeIngredient(BrewerInventory inventory, Player player) {
|
||||
ItemStack ingredient = inventory.getIngredient();
|
||||
|
||||
if (isEmpty(ingredient) || !isValidIngredient(player, ingredient)) {
|
||||
return false;
|
||||
}
|
||||
else if (ingredient.getAmount() <= 1) {
|
||||
inventory.setItem(INGREDIENT_SLOT, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ingredient.setAmount(ingredient.getAmount() - 1);
|
||||
inventory.setItem(INGREDIENT_SLOT, ingredient);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidIngredient(Player player, ItemStack item) {
|
||||
if (isEmpty(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ItemStack ingredient : getValidIngredients(player)) {
|
||||
if (item.isSimilar(ingredient)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<ItemStack> getValidIngredients(Player player) {
|
||||
if (player == null || !Permissions.concoctions(player)) {
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierOne;
|
||||
}
|
||||
|
||||
switch (UserManager.getPlayer(player).getAlchemyManager().getTier()) {
|
||||
case 5:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierFive;
|
||||
case 4:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierFour;
|
||||
case 3:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierThree;
|
||||
case 2:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierTwo;
|
||||
default:
|
||||
return PotionConfig.getInstance().concoctionsIngredientsTierOne;
|
||||
}
|
||||
}
|
||||
|
||||
public static void finishBrewing(Block brewingStand, Player player, boolean forced) {
|
||||
if (!(brewingStand.getState() instanceof BrewingStand)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BrewerInventory inventory = ((BrewingStand) brewingStand.getState()).getInventory();
|
||||
ItemStack ingredient = inventory.getIngredient() == null ? null : inventory.getIngredient().clone();
|
||||
|
||||
if (removeIngredient(inventory, player)) {
|
||||
for (int bottle : BOTTLE_SLOTS) {
|
||||
if (!isEmpty(inventory.getItem(bottle)) && PotionConfig.getInstance().potionMap.containsKey(inventory.getItem(bottle).getDurability())) {
|
||||
AlchemyPotion input = PotionConfig.getInstance().potionMap.get(inventory.getItem(bottle).getDurability());
|
||||
AlchemyPotion output = PotionConfig.getInstance().potionMap.get(input.getChildDataValue(ingredient));
|
||||
|
||||
if (output != null) {
|
||||
inventory.setItem(bottle, output.toItemStack(inventory.getItem(bottle).getAmount()).clone());
|
||||
|
||||
if (player != null) {
|
||||
UserManager.getPlayer(player).getAlchemyManager().handlePotionBrewSuccesses(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!forced) {
|
||||
scheduleUpdate(inventory);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean transferOneItem(InventoryView view, int fromSlot, int toSlot) {
|
||||
ItemStack from = view.getItem(fromSlot).clone();
|
||||
ItemStack to = view.getItem(toSlot).clone();
|
||||
|
||||
if (isEmpty(from)) {
|
||||
return false;
|
||||
}
|
||||
else if (!isEmpty(to) && from.getAmount() >= from.getType().getMaxStackSize()) {
|
||||
return false;
|
||||
}
|
||||
else if (isEmpty(to) || from.isSimilar(to)) {
|
||||
if (isEmpty(to)) {
|
||||
to = from.clone();
|
||||
to.setAmount(1);
|
||||
}
|
||||
else {
|
||||
to.setAmount(to.getAmount() + 1);
|
||||
}
|
||||
|
||||
from.setAmount(from.getAmount() - 1);
|
||||
view.setItem(toSlot, isEmpty(to) ? null : to);
|
||||
view.setItem(fromSlot, isEmpty(from) ? null : from);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer items between two ItemStacks, returning the leftover status
|
||||
*/
|
||||
private static boolean transferItems(InventoryView view, int fromSlot, int toSlot) {
|
||||
if (isEmpty(view.getItem(fromSlot))) {
|
||||
return false;
|
||||
}
|
||||
else if (isEmpty(view.getItem(toSlot))) {
|
||||
view.setItem(toSlot, view.getItem(fromSlot).clone());
|
||||
view.setItem(fromSlot, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (view.getItem(fromSlot).isSimilar(view.getItem(toSlot))) {
|
||||
if (view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount() > view.getItem(toSlot).getType().getMaxStackSize()) {
|
||||
int left = view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount() - view.getItem(toSlot).getType().getMaxStackSize();
|
||||
|
||||
ItemStack to = new ItemStack(view.getItem(toSlot));
|
||||
to.setAmount(to.getType().getMaxStackSize());
|
||||
view.setItem(toSlot, to);
|
||||
|
||||
ItemStack from = new ItemStack(view.getItem(fromSlot));
|
||||
from.setAmount(left);
|
||||
view.setItem(fromSlot, from);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ItemStack to = new ItemStack(view.getItem(toSlot));
|
||||
to.setAmount(view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount());
|
||||
view.setItem(fromSlot, null);
|
||||
view.setItem(toSlot, to);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void scheduleCheck(Player player, BrewingStand brewingStand) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(mcMMO.p, new AlchemyBrewCheckTask(player, brewingStand));
|
||||
}
|
||||
|
||||
private static void scheduleUpdate(Inventory inventory) {
|
||||
for (HumanEntity he : inventory.getViewers()) {
|
||||
if (he instanceof Player) {
|
||||
scheduleUpdate((Player) he);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void scheduleUpdate(Player player) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(mcMMO.p, new PlayerUpdateInventoryTask(player));
|
||||
}
|
||||
|
||||
public static void handleInventoryClick(InventoryClickEvent event) {
|
||||
Player player = event.getWhoClicked() instanceof Player ? (Player) event.getWhoClicked() : null;
|
||||
BrewingStand brewingStand = (BrewingStand) event.getInventory().getHolder();
|
||||
|
||||
ItemStack cursor = event.getCursor();
|
||||
ItemStack clicked = event.getCurrentItem();
|
||||
|
||||
if (clicked != null && clicked.getType() == Material.POTION) {
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
if (event.isShiftClick()) {
|
||||
if (event.getSlotType() == SlotType.FUEL) {
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (event.getSlotType() == SlotType.CONTAINER || event.getSlotType() == SlotType.QUICKBAR) {
|
||||
if (isValidIngredient(player, clicked)) {
|
||||
if (event.isLeftClick()) {
|
||||
transferItems(event.getView(), event.getRawSlot(), INGREDIENT_SLOT);
|
||||
}
|
||||
else if (event.isRightClick()) {
|
||||
transferOneItem(event.getView(), event.getRawSlot(), INGREDIENT_SLOT);
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
scheduleUpdate(brewingStand.getInventory());
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event.getRawSlot() == INGREDIENT_SLOT) {
|
||||
if (isEmpty(cursor) && isEmpty(clicked)) {
|
||||
return;
|
||||
}
|
||||
else if (isEmpty(cursor)) {
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (isEmpty(clicked)) {
|
||||
if (isValidIngredient(player, event.getCursor())) {
|
||||
if (event.getClick() == ClickType.LEFT || (event.getClick() == ClickType.RIGHT && event.getCursor().getAmount() == 1)) {
|
||||
event.setCancelled(true);
|
||||
|
||||
event.setCurrentItem(event.getCursor().clone());
|
||||
event.setCursor(null);
|
||||
|
||||
scheduleUpdate(brewingStand.getInventory());
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (event.getClick() == ClickType.RIGHT) {
|
||||
event.setCancelled(true);
|
||||
|
||||
ItemStack one = event.getCursor().clone();
|
||||
one.setAmount(1);
|
||||
|
||||
ItemStack rest = event.getCursor().clone();
|
||||
rest.setAmount(event.getCursor().getAmount() - 1);
|
||||
|
||||
event.setCurrentItem(one);
|
||||
event.setCursor(rest);
|
||||
|
||||
scheduleUpdate(brewingStand.getInventory());
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleInventoryDrag(InventoryDragEvent event) {
|
||||
Player player = event.getWhoClicked() instanceof Player ? (Player) event.getWhoClicked() : null;
|
||||
BrewingStand brewingStand = (BrewingStand) event.getInventory().getHolder();
|
||||
|
||||
ItemStack cursor = event.getCursor();
|
||||
ItemStack ingredient = brewingStand.getInventory().getIngredient();
|
||||
|
||||
if (event.getInventorySlots().contains(INGREDIENT_SLOT)) {
|
||||
if (isEmpty(ingredient) || ingredient.isSimilar(cursor)) {
|
||||
if (isValidIngredient(player, cursor)) {
|
||||
// Not handled: dragging custom ingredients over ingredient slot (does not trigger any event)
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
event.setCancelled(true);
|
||||
|
||||
scheduleUpdate(brewingStand.getInventory());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleInventoryMoveItem(InventoryMoveItemEvent event) {
|
||||
Player player = null;
|
||||
BrewingStand brewingStand = (BrewingStand) event.getDestination().getHolder();
|
||||
|
||||
if (isValidIngredient(player, event.getItem())) {
|
||||
scheduleCheck(player, brewingStand);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
event.setCancelled(true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user