    001    /**
    002     * This software is provided under the terms of the Minecraft Forge Public
    003     * License v1.0.
    004     */
    005
    006    package net.minecraftforge.common;
    007
    008    import java.util.ArrayList;
    009    import java.util.Arrays;
    010
    011    import net.minecraft.util.DamageSource;
    012    import net.minecraft.entity.EntityLiving;
    013    import net.minecraft.entity.player.EntityPlayer;
    014    import net.minecraft.item.ItemArmor;
    015    import 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     */
    024    public 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[0]);
    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 / (double)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    }