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}