<commit_before>package com.elmakers.mine.bukkit.item;

import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import com.elmakers.mine.bukkit.api.magic.CasterProperties;
import com.elmakers.mine.bukkit.api.magic.Mage;
import com.elmakers.mine.bukkit.api.magic.MageController;
import com.elmakers.mine.bukkit.api.magic.Messages;
import com.elmakers.mine.bukkit.api.spell.CostReducer;
import com.elmakers.mine.bukkit.api.wand.Wand;
import com.elmakers.mine.bukkit.integration.VaultController;
import com.elmakers.mine.bukkit.utility.CompatibilityUtils;

public class Cost implements com.elmakers.mine.bukkit.api.item.Cost {
    protected enum Type {
        ITEM,
        XP,
        SP,
        MANA,
        CURRENCY,
        HEALTH,
        HUNGER,
        LEVELS
    };

    protected ItemStack item;
    protected boolean itemWildcard;
    protected double amount;
    protected Type type;

    public Cost(MageController controller, String key, double cost)
    {
        this.amount = cost;
        if (key.toLowerCase().equals("xp")) {
            this.type = Type.XP;
        } else if (key.toLowerCase().equals("sp")) {
            this.type = Type.SP;
        } else if (key.toLowerCase().equals("mana")) {
            this.type = Type.MANA;
        } else if (key.toLowerCase().equals("currency")) {
            this.type = Type.CURRENCY;
        } else if (key.toLowerCase().equals("health")) {
            this.type = Type.HEALTH;
        } else if (key.toLowerCase().equals("hunger")) {
            this.type = Type.HUNGER;
        } else if (key.toLowerCase().equals("levels")) {
            this.type = Type.LEVELS;
        } else {
            if (key.endsWith(":*")) {
                key = key.substring(0,key.length() - 2);
                itemWildcard = true;
            } else {
                itemWildcard = false;
            }
            this.item = controller.createItem(key, true);
            if (this.item != null) {
                this.item.setAmount((int)Math.ceil(amount));
            }
            this.type = Type.ITEM;
        }
    }

    @Override
    public boolean isEmpty(CostReducer reducer) {
        switch (this.type) {
            case ITEM:
                return item == null || getReducedCost(item.getAmount(), reducer) == 0;
            case LEVELS:
                return getLevels(reducer) == 0;
            case XP:
                return getXP(reducer) == 0;
            case MANA:
                return getMana(reducer) == 0;
            case CURRENCY:
                return getCurrency(reducer) == 0;
            case SP:
                return getSkillPoints(reducer) == 0;
            default:
                return getReducedCost(amount, reducer) == 0;
        }
    }

    @Override
    public boolean has(Mage mage, CasterProperties caster, CostReducer reducer) {

        Player player = mage.getPlayer();
        switch (type) {
            case ITEM:
                return isConsumeFree(reducer) || mage.hasItem(getItemStack(reducer), itemWildcard);
            case XP:
                return mage.getExperience() >= getXP(reducer);
            case LEVELS:
                return player != null && player.getLevel() >= getLevels(reducer);
            case MANA:
                return caster == null ? mage.getMana() >= getMana(reducer) : caster.getMana() >= getMana(reducer);
            case CURRENCY:
                VaultController vault = VaultController.getInstance();
                return vault.has(mage.getPlayer(), getCurrency(reducer));
            case SP:
                return mage.getSkillPoints() >= getSkillPoints(reducer);
            case HEALTH:
                LivingEntity living = mage.getLivingEntity();
                return living != null && living.getHealth() >= getReducedCost(amount, reducer);
            case HUNGER:
                return player != null && player.getFoodLevel() >= getReducedCost(amount, reducer);
        }

        return false;
    }

    @Override
    public boolean has(Mage mage, Wand wand, CostReducer reducer) {
        return has(mage, (CasterProperties)wand, reducer);
    }

    @Override
    public boolean has(Mage mage) {
        return has(mage, mage.getActiveWand(), null);
    }

    @Override
    public void deduct(Mage mage, CasterProperties caster, CostReducer reducer) {
        Player player = mage.getPlayer();
        switch (type) {
            case ITEM:
                if (!isConsumeFree(reducer)) {
                    ItemStack itemStack = getItemStack(reducer);
                    mage.removeItem(itemStack, itemWildcard);
                }
                break;
            case XP:
                mage.removeExperience(getXP(reducer));
                break;
            case LEVELS:
                if (player != null) {
                    int newLevel = Math.max(0, player.getLevel() - getLevels(reducer));
                    player.setLevel(newLevel);
                }
                break;
            case MANA:
                if (caster != null) {
                    caster.removeMana(getMana(reducer));
                } else {
                    mage.removeMana(getMana(reducer));
                }
                break;
            case CURRENCY:
                VaultController vault = VaultController.getInstance();
                vault.withdrawPlayer(mage.getPlayer(), getCurrency(reducer));
                break;
            case SP:
                mage.addSkillPoints(-getSkillPoints(reducer));
                break;
            case HEALTH:
                LivingEntity living = mage.getLivingEntity();
                if (living != null) {
                    living.setHealth(Math.max(0, living.getHealth() - getReducedCost(amount, reducer)));
                }
                break;
            case HUNGER:
                if (player != null) {
                    player.setFoodLevel(Math.max(0, player.getFoodLevel() - getRoundedCost(amount, reducer)));
                }
                break;
        }
    }

    @Override
    public void deduct(Mage mage, Wand wand, CostReducer reducer) {
        deduct(mage, (CasterProperties)wand, reducer);
    }

    @Override
    public void deduct(Mage mage) {
        deduct(mage, mage.getActiveWand(), null);
    }

    protected int getRoundedCost(double cost, CostReducer reducer) {
        return (int)Math.ceil(getReducedCost(cost, reducer));
    }

    protected double getReducedCost(double cost, CostReducer reducer)
    {
        double reducedAmount = cost;
        double reduction = reducer == null ? 0 : reducer.getCostReduction();
        if (reduction >= 1) {
            return 0;
        }
        if (reduction > 0) {
            reducedAmount = (1.0f - reduction) * reducedAmount;
        }
        if (reducer != null) {
            reducedAmount = reducedAmount * reducer.getCostScale();
        }
        return reducedAmount;
    }

    public int getAmount(CostReducer reducer)<commit_msg>Allow for negative cost reduction<commit_after>package com.elmakers.mine.bukkit.item;

import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import com.elmakers.mine.bukkit.api.magic.CasterProperties;
import com.elmakers.mine.bukkit.api.magic.Mage;
import com.elmakers.mine.bukkit.api.magic.MageController;
import com.elmakers.mine.bukkit.api.magic.Messages;
import com.elmakers.mine.bukkit.api.spell.CostReducer;
import com.elmakers.mine.bukkit.api.wand.Wand;
import com.elmakers.mine.bukkit.integration.VaultController;
import com.elmakers.mine.bukkit.utility.CompatibilityUtils;

public class Cost implements com.elmakers.mine.bukkit.api.item.Cost {
    protected enum Type {
        ITEM,
        XP,
        SP,
        MANA,
        CURRENCY,
        HEALTH,
        HUNGER,
        LEVELS
    };

    protected ItemStack item;
    protected boolean itemWildcard;
    protected double amount;
    protected Type type;

    public Cost(MageController controller, String key, double cost)
    {
        this.amount = cost;
        if (key.toLowerCase().equals("xp")) {
            this.type = Type.XP;
        } else if (key.toLowerCase().equals("sp")) {
            this.type = Type.SP;
        } else if (key.toLowerCase().equals("mana")) {
            this.type = Type.MANA;
        } else if (key.toLowerCase().equals("currency")) {
            this.type = Type.CURRENCY;
        } else if (key.toLowerCase().equals("health")) {
            this.type = Type.HEALTH;
        } else if (key.toLowerCase().equals("hunger")) {
            this.type = Type.HUNGER;
        } else if (key.toLowerCase().equals("levels")) {
            this.type = Type.LEVELS;
        } else {
            if (key.endsWith(":*")) {
                key = key.substring(0,key.length() - 2);
                itemWildcard = true;
            } else {
                itemWildcard = false;
            }
            this.item = controller.createItem(key, true);
            if (this.item != null) {
                this.item.setAmount((int)Math.ceil(amount));
            }
            this.type = Type.ITEM;
        }
    }

    @Override
    public boolean isEmpty(CostReducer reducer) {
        switch (this.type) {
            case ITEM:
                return item == null || getReducedCost(item.getAmount(), reducer) == 0;
            case LEVELS:
                return getLevels(reducer) == 0;
            case XP:
                return getXP(reducer) == 0;
            case MANA:
                return getMana(reducer) == 0;
            case CURRENCY:
                return getCurrency(reducer) == 0;
            case SP:
                return getSkillPoints(reducer) == 0;
            default:
                return getReducedCost(amount, reducer) == 0;
        }
    }

    @Override
    public boolean has(Mage mage, CasterProperties caster, CostReducer reducer) {

        Player player = mage.getPlayer();
        switch (type) {
            case ITEM:
                return isConsumeFree(reducer) || mage.hasItem(getItemStack(reducer), itemWildcard);
            case XP:
                return mage.getExperience() >= getXP(reducer);
            case LEVELS:
                return player != null && player.getLevel() >= getLevels(reducer);
            case MANA:
                return caster == null ? mage.getMana() >= getMana(reducer) : caster.getMana() >= getMana(reducer);
            case CURRENCY:
                VaultController vault = VaultController.getInstance();
                return vault.has(mage.getPlayer(), getCurrency(reducer));
            case SP:
                return mage.getSkillPoints() >= getSkillPoints(reducer);
            case HEALTH:
                LivingEntity living = mage.getLivingEntity();
                return living != null && living.getHealth() >= getReducedCost(amount, reducer);
            case HUNGER:
                return player != null && player.getFoodLevel() >= getReducedCost(amount, reducer);
        }

        return false;
    }

    @Override
    public boolean has(Mage mage, Wand wand, CostReducer reducer) {
        return has(mage, (CasterProperties)wand, reducer);
    }

    @Override
    public boolean has(Mage mage) {
        return has(mage, mage.getActiveWand(), null);
    }

    @Override
    public void deduct(Mage mage, CasterProperties caster, CostReducer reducer) {
        Player player = mage.getPlayer();
        switch (type) {
            case ITEM:
                if (!isConsumeFree(reducer)) {
                    ItemStack itemStack = getItemStack(reducer);
                    mage.removeItem(itemStack, itemWildcard);
                }
                break;
            case XP:
                mage.removeExperience(getXP(reducer));
                break;
            case LEVELS:
                if (player != null) {
                    int newLevel = Math.max(0, player.getLevel() - getLevels(reducer));
                    player.setLevel(newLevel);
                }
                break;
            case MANA:
                if (caster != null) {
                    caster.removeMana(getMana(reducer));
                } else {
                    mage.removeMana(getMana(reducer));
                }
                break;
            case CURRENCY:
                VaultController vault = VaultController.getInstance();
                vault.withdrawPlayer(mage.getPlayer(), getCurrency(reducer));
                break;
            case SP:
                mage.addSkillPoints(-getSkillPoints(reducer));
                break;
            case HEALTH:
                LivingEntity living = mage.getLivingEntity();
                if (living != null) {
                    living.setHealth(Math.max(0, living.getHealth() - getReducedCost(amount, reducer)));
                }
                break;
            case HUNGER:
                if (player != null) {
                    player.setFoodLevel(Math.max(0, player.getFoodLevel() - getRoundedCost(amount, reducer)));
                }
                break;
        }
    }

    @Override
    public void deduct(Mage mage, Wand wand, CostReducer reducer) {
        deduct(mage, (CasterProperties)wand, reducer);
    }

    @Override
    public void deduct(Mage mage) {
        deduct(mage, mage.getActiveWand(), null);
    }

    protected int getRoundedCost(double cost, CostReducer reducer) {
        return (int)Math.ceil(getReducedCost(cost, reducer));
    }

    protected double getReducedCost(double cost, CostReducer reducer)
    {
        double reducedAmount = cost;
        double reduction = reducer == null ? 0 : reducer.getCostReduction();
        if (reduction >= 1) {
            return 0;
        }
        if (reduction != 0) {
            reducedAmount = (1.0f - reduction) * reducedAmount;
        }
        if (reducer != null) {
            reducedAmount = reducedAmount * reducer.getCostScale();
        }
        return reducedAmount;
    }

    public int getAmount(CostReducer reducer)
{'fraction_non_alphanumeric': 0.10630117479530082, 'fraction_numerical': 0.002563189747241011, 'mean_word_length': 1.7557386698057682, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2012074', 'n_tokens_mistral': 4185, 'n_tokens_neox': 3899, 'n_words': 821}