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[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    }