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 }