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 }