    001/**
    002 * This software is provided under the terms of the Minecraft Forge Public
    003 * License v1.0.
    004 */
    005
    006package net.minecraftforge.common;
    007
    008import java.util.ArrayList;
    009import java.util.Arrays;
    010
    011import net.minecraft.util.DamageSource;
    012import net.minecraft.entity.EntityLiving;
    013import net.minecraft.entity.player.EntityPlayer;
    014import net.minecraft.item.ItemArmor;
    015import net.minecraft.item.ItemStack;
    016
    017/**
    018 * This interface is to be implemented by ItemArmor classes. It will allow to
    019 * modify computation of damage and health loss. Computation will be called
    020 * before the actual armor computation, which can then be cancelled.
    021 *
    022 * @see ItemArmor
    023 */
    024public interface ISpecialArmor
    025{
    026    /**
    027     * Retrieves the modifiers to be used when calculating armor damage.
    028     *
    029     * Armor will higher priority will have damage applied to them before
    030     * lower priority ones. If there are multiple pieces of armor with the
    031     * same priority, damage will be distributed between them based on there
    032     * absorption ratio.
    033     *
    034     * @param player The entity wearing the armor.
    035     * @param armor The ItemStack of the armor item itself.
    036     * @param source The source of the damage, which can be used to alter armor
    037     *     properties based on the type or source of damage.
    038     * @param damage The total damage being applied to the entity
    039     * @param slot The armor slot the item is in.
    040     * @return A ArmorProperties instance holding information about how the armor effects damage.
    041     */
    042    public ArmorProperties getProperties(EntityLiving player, ItemStack armor, DamageSource source, double damage, int slot);
    043
    044    /**
    045     * Get the displayed effective armor.
    046     *
    047     * @param player The player wearing the armor.
    048     * @param armor The ItemStack of the armor item itself.
    049     * @param slot The armor slot the item is in.
    050     * @return The number of armor points for display, 2 per shield.
    051     */
    052    public abstract int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot);
    053
    054    /**
    055     * Applies damage to the ItemStack. The mod is responsible for reducing the
    056     * item durability and stack size. If the stack is depleted it will be cleaned
    057     * up automatically.
    058     *
    059     * @param entity The entity wearing the armor
    060     * @param stack The ItemStack of the armor item itself.
    061     * @param source The source of the damage, which can be used to alter armor
    062     *     properties based on the type or source of damage.
    063     * @param damage The amount of damage being applied to the armor
    064     * @param slot The armor slot the item is in.
    065     */
    066    public abstract void damageArmor(EntityLiving entity, ItemStack stack, DamageSource source, int damage, int slot);
    067
    068    public static class ArmorProperties implements Comparable<ArmorProperties>
    069    {
    070        public int    Priority    = 0;
    071        public int    AbsorbMax   = Integer.MAX_VALUE;
    072        public double AbsorbRatio = 0;
    073        public int    Slot        = 0;
    074        private static final boolean DEBUG = false; //Only enable this if you wish to be spamed with debugging information.
    075                                                    //Left it in because I figured it'd be useful for modders developing custom armor.
    076
    077        public ArmorProperties(int priority, double ratio, int max)
    078        {
    079            Priority    = priority;
    080            AbsorbRatio = ratio;
    081            AbsorbMax   = max;
    082        }
    083
    084        /**
    085         * Gathers and applies armor reduction to damage being dealt to a entity.
    086         *
    087         * @param entity The Entity being damage
    088         * @param inventory An array of armor items
    089         * @param source The damage source type
    090         * @param damage The total damage being done
    091         * @return The left over damage that has not been absorbed by the armor
    092         */
    093        public static int ApplyArmor(EntityLiving entity, ItemStack[] inventory, DamageSource source, double damage)
    094        {
    095            if (DEBUG)
    096            {
    097                System.out.println("Start: " + damage + " " + (damage * 25));
    098            }
    099            damage *= 25;
    100            ArrayList<ArmorProperties> dmgVals = new ArrayList<ArmorProperties>();
    101            for (int x = 0; x < inventory.length; x++)
    102            {
    103                ItemStack stack = inventory[x];
    104                if (stack == null)
    105                {
    106                    continue;
    107                }
    108                ArmorProperties prop = null;
    109                if (stack.getItem() instanceof ISpecialArmor)
    110                {
    111                    ISpecialArmor armor = (ISpecialArmor)stack.getItem();
    112                    prop = armor.getProperties(entity, stack, source, damage / 25D, x).copy();
    113                }
    114                else if (stack.getItem() instanceof ItemArmor && !source.isUnblockable())
    115                {
    116                    ItemArmor armor = (ItemArmor)stack.getItem();
    117                    prop = new ArmorProperties(0, armor.damageReduceAmount / 25D, armor.getMaxDamage() + 1 - stack.getItemDamage());
    118                }
    119                if (prop != null)
    120                {
    121                    prop.Slot = x;
    122                    dmgVals.add(prop);
    123                }
    124            }
    125            if (dmgVals.size() > 0)
    126            {
    127                ArmorProperties[] props = dmgVals.toArray(new ArmorProperties[dmgVals.size()]);
    128                StandardizeList(props, damage);
    129                int level = props[0].Priority;
    130                double ratio = 0;
    131                for (ArmorProperties prop : props)
    132                {
    133                    if (level != prop.Priority)
    134                    {
    135                        damage -= (damage * ratio);
    136                        ratio = 0;
    137                        level = prop.Priority;
    138                    }
    139                    ratio += prop.AbsorbRatio;
    140
    141                    double absorb = damage * prop.AbsorbRatio;
    142                    if (absorb > 0)
    143                    {
    144                        ItemStack stack = inventory[prop.Slot];
    145                        int itemDamage = (int)(absorb / 25D < 1 ? 1 : absorb / 25D);
    146                        if (stack.getItem() instanceof ISpecialArmor)
    147                        {
    148                            ((ISpecialArmor)stack.getItem()).damageArmor(entity, stack, source, itemDamage, prop.Slot);
    149                        }
    150                        else
    151                        {
    152                            if (DEBUG)
    153                            {
    154                                System.out.println("Item: " + stack.toString() + " Absorbed: " + (absorb / 25D) + " Damaged: " + itemDamage);
    155                            }
    156                            stack.damageItem(itemDamage, entity);
    157                        }
    158                        if (stack.stackSize <= 0)
    159                        {
    160                            /*if (entity instanceof EntityPlayer)
    161                            {
    162                                stack.onItemDestroyedByUse((EntityPlayer)entity);
    163                            }*/
    164                            inventory[prop.Slot] = null;
    165                        }
    166                    }
    167                }
    168                damage -= (damage * ratio);
    169            }
    170            damage += entity.carryoverDamage;
    171            if (DEBUG)
    172            {
    173                System.out.println("Return: " + (int)(damage / 25D) + " " + damage);
    174            }
    175            entity.carryoverDamage = (int)damage % 25;
    176            return (int)(damage / 25D);
    177        }
    178
    179        /**
    180         * Sorts and standardizes the distribution of damage over armor.
    181         *
    182         * @param armor The armor information
    183         * @param damage The total damage being received
    184         */
    185        private static void StandardizeList(ArmorProperties[] armor, double damage)
    186        {
    187            Arrays.sort(armor);
    188
    189            int     start     = 0;
    190            double  total     = 0;
    191            int     priority  = armor[0].Priority;
    192            int     pStart    = 0;
    193            boolean pChange   = false;
    194            boolean pFinished = false;
    195
    196            if (DEBUG)
    197            {
    198                for (ArmorProperties prop : armor)
    199                {
    200                    System.out.println(prop);
    201                }
    202                System.out.println("========================");
    203            }
    204
    205            for (int x = 0; x < armor.length; x++)
    206            {
    207                total += armor[x].AbsorbRatio;
    208                if (x == armor.length - 1 || armor[x].Priority != priority)
    209                {
    210                    if (armor[x].Priority != priority)
    211                    {
    212                        total -= armor[x].AbsorbRatio;
    213                        x--;
    214                        pChange = true;
    215                    }
    216                    if (total > 1)
    217                    {
    218                        for (int y = start; y <= x; y++)
    219                        {
    220                            double newRatio = armor[y].AbsorbRatio / total;
    221                            if (newRatio * damage > armor[y].AbsorbMax)
    222                            {
    223                                armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
    224                                total = 0;
    225                                for (int z = pStart; z <= y; z++)
    226                                {
    227                                    total += armor[z].AbsorbRatio;
    228                                }
    229                                start = y + 1;
    230                                x = y;
    231                                break;
    232                            }
    233                            else
    234                            {
    235                                armor[y].AbsorbRatio = newRatio;
    236                                pFinished = true;
    237                            }
    238                        }
    239                        if (pChange && pFinished)
    240                        {
    241                            damage -= (damage * total);
    242                            total = 0;
    243                            start = x + 1;
    244                            priority = armor[start].Priority;
    245                            pStart = start;
    246                            pChange = false;
    247                            pFinished = false;
    248                            if (damage <= 0)
    249                            {
    250                                for (int y = x + 1; y < armor.length; y++)
    251                                {
    252                                    armor[y].AbsorbRatio = 0;
    253                                }
    254                                break;
    255                            }
    256                        }
    257                    }
    258                    else
    259                    {
    260                        for (int y = start; y <= x; y++)
    261                        {
    262                            total -= armor[y].AbsorbRatio;
    263                            if (damage * armor[y].AbsorbRatio > armor[y].AbsorbMax)
    264                            {
    265                                armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
    266                            }
    267                            total += armor[y].AbsorbRatio;
    268                        }
    269                        damage -= (damage * total);
    270                        total = 0;
    271                        if (x != armor.length - 1)
    272                        {
    273                            start = x + 1;
    274                            priority = armor[start].Priority;
    275                            pStart = start;
    276                            pChange = false;
    277                            if (damage <= 0)
    278                            {
    279                                for (int y = x + 1; y < armor.length; y++)
    280                                {
    281                                    armor[y].AbsorbRatio = 0;
    282                                }
    283                                break;
    284                            }
    285                        }
    286                    }
    287                }
    288            }
    289            if (DEBUG)
    290            {
    291                for (ArmorProperties prop : armor)
    292                {
    293                    System.out.println(prop);
    294                }
    295            }
    296        }
    297
    298        public int compareTo(ArmorProperties o)
    299        {
    300            if (o.Priority != Priority)
    301            {
    302                return o.Priority - Priority;
    303            }
    304            double left =  (  AbsorbRatio == 0 ? 0 :   AbsorbMax * 100.0D /   AbsorbRatio);
    305            double right = (o.AbsorbRatio == 0 ? 0 : o.AbsorbMax * 100.0D / o.AbsorbRatio);
    306            return (int)(left - right);
    307        }
    308
    309        public String toString()
    310        {
    311            return String.format("%d, %d, %f, %d", Priority, AbsorbMax, AbsorbRatio, (AbsorbRatio == 0 ? 0 : (int)(AbsorbMax * 100.0D / AbsorbRatio)));
    312        }
    313
    314        public ArmorProperties copy()
    315        {
    316            return new ArmorProperties(Priority, AbsorbRatio, AbsorbMax);
    317        }
    318    }
    319}