[SOLVED] ISpecialArmor ArmorProperties either gives invincibility or no protection

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
SEE LAST POST FOR SOLUTION

I ran into this issue a bit ago with a different set of armor. If you implement ISpecialAmor, then when overriding getProperties, which returns an ArmorProperties object which determines damage absorption, you get either full invincibility or no protection at all. I got it working, but looking back at the code, I can't tell what exactly is different from what I have now.
For the curious, I'm trying to make mid-game powered armor. :p
Code for WORKING armorset (a rather unrealistic Hazmat Suit):
Code:
//Imports 'n' stuff

public class ItemHazmatArmor extends ItemArmorXF implements ISpecialArmor {
    public static ArmorMaterial HAZMATARMOR = EnumHelper.addArmorMaterial("HAZMATARMOR", 12, new int[] {25, 25, 25, 25}, 7);
  
    public ItemHazmatArmor(String unlocalizedName, ArmorMaterial material, int armorSetNumber, int armorPiece) {
        super(unlocalizedName, HAZMATARMOR, armorSetNumber, armorPiece);
    }

    @Override
    public void onArmorTick(World world, EntityPlayer player, ItemStack armor) {
        //Helmet gives you water breathing
        if (ModItems.itemHazmatHelmet == armor.getItem()) {
            player.addPotionEffect(new PotionEffect(Potion.waterBreathing.id, 1, 0));
        }
    }
  
    //Different armor pieces give different amounts of protection against different damage types
    //The armor properties is returned as (1 - use this armor first, (1-(percent damage taken)), Integer.MAX_VALUE)
    @Override
    public ArmorProperties getProperties(EntityLivingBase player,
            ItemStack armor, DamageSource source, double damage, int slot) {
        //Take damage first; damageArmor is derpy in how it is called
        if (source == source.inFire || source == source.lava || source == source.onFire || source == source.fall || source == source.cactus || source == source.wither || source == source.magic || !source.isUnblockable()) {
            damageArmor(player, armor, source, 1, slot);
        }
      
        //Chestplate protects against fire/lava
        if (ModItems.itemHazmatChestplate == armor.getItem() && (source == source.inFire || source == source.lava || source == source.onFire)) {
            //Take 1/3 fire/lava damage
            return new ArmorProperties(1, .67, Integer.MAX_VALUE);
        //Boots protect against fall damage
        } else if (ModItems.itemHazmatBoots == armor.getItem() && source == source.fall) {
            //Take 2/3 fall damage
            return new ArmorProperties(1, .34, Integer.MAX_VALUE);
        //Leggings protect against cactus, wither, and "magic"
        } else if (ModItems.itemHazmatLeggings == armor.getItem() && (source == source.cactus || source == source.wither || source == source.magic)){
            //Take 1/3 cactus/wither/magic damage
            return new ArmorProperties(1, .67, Integer.MAX_VALUE);
        } else if (!source.isUnblockable()){
            return new ArmorProperties(1, 0, 0);
        } else {
            return new ArmorProperties(0, 0, 0);
        }
    }

    @Override
    public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot) {
        //Always return zero 'cause it's just a hazmat suit; no protection offered. :P
        return 0;
    }

    @Override
    public void damageArmor(EntityLivingBase entity, ItemStack stack,
            DamageSource source, int damage, int slot) {
        stack.damageItem(1, entity);
    }
}
Code for the NOT WORKING armorset:
Code:
//Imports 'n' stuff

public class ItemPoweredArmor extends ItemArmorXF implements ISpecialArmor, IEnergyContainerItem {
    //Armor material
    public static ArmorMaterial POWEREDARMOR = EnumHelper.addArmorMaterial("POWEREDARMOR", 12, new int[] {3, 7, 5, 2}, 10);
    public static double maxProtect = .75;
    public static double minProtect = .1;
  
    public static int capacity = 10000;
    public static int maxTransfer = 100;
    public static int durability = 300;
    public static int energyPerHit = capacity / durability;
  
    public ItemPoweredArmor(String unlocalizedName, ArmorMaterial material, int armorSetNumber, int armorPiece) {
        super(unlocalizedName, POWEREDARMOR, armorSetNumber, armorPiece);
    }
  
    //Set tooltip
    @Override
    public void addInformation(ItemStack stack, EntityPlayer player, List list, boolean par4) {
        String str = "";
        int storedEnergy = getEnergyStored(stack);
        str = String.valueOf(storedEnergy) + "/" + getMaxEnergyStored(stack) + " RF";
        list.add(str);
    }
  
    public double getEnergyStoredPercent(ItemStack stack) {
        double energyStored = getEnergyStored(stack);
        double maxEnergyStored = getMaxEnergyStored(stack);
        double percentStored = energyStored / maxEnergyStored;
        return (percentStored);
    }
  
    //Pasted from KingLemming's ItemEnergyContainer code, with minor adjustments as needed
    @Override
    public int receiveEnergy(ItemStack container, int maxReceive,
            boolean simulate) {
        if (container.stackTagCompound == null) {
            container.stackTagCompound = new NBTTagCompound();
        }
        int energy = container.stackTagCompound.getInteger("Energy");
        int energyReceived = Math.min(capacity - energy, Math.min(this.maxTransfer, maxReceive));

        if (!simulate) {
            energy += energyReceived;
            container.stackTagCompound.setInteger("Energy", energy);
        }
        return energyReceived;
    }

    //Pasted from KingLemming's ItemEnergyContainer code, with minor adjustments as needed
    @Override
    public int extractEnergy(ItemStack container, int maxExtract,
            boolean simulate) {
        if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
            return 0;
        }
        int energy = container.stackTagCompound.getInteger("Energy");
        int energyExtracted = Math.min(energy, Math.min(this.maxTransfer, maxExtract));

        if (!simulate) {
            energy -= energyExtracted;
            container.stackTagCompound.setInteger("Energy", energy);
        }
        return energyExtracted;
    }

    //Pasted from KingLemming's ItemEnergyContainer code, with minor adjustments as needed
    @Override
    public int getEnergyStored(ItemStack container) {
        if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
            return 0;
        }
        return container.stackTagCompound.getInteger("Energy");
    }

    @Override
    public int getMaxEnergyStored(ItemStack container) {
        return capacity;
    }

    @Override
    public ArmorProperties getProperties(EntityLivingBase player,
            ItemStack armor, DamageSource source, double damage, int slot) {
        if (!source.isUnblockable()) {
            double absorbRatio = getEnergyStoredPercent(armor);
            LogHelper.info(absorbRatio);
            //Make sure that the absorb ratio is always at least the minimum protect if there is energy left
            //but also that it is never above the max
            if (getEnergyStored(armor) != 0) {
                absorbRatio += minProtect;
            }
            if (absorbRatio > maxProtect) {
                absorbRatio = maxProtect;
            }
            LogHelper.info(absorbRatio);
            return new ArmorProperties(1, absorbRatio, Integer.MAX_VALUE);
        } else {
            return new ArmorProperties(0, 0, 0);
        }
    }

    @Override
    public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot) {
        //Armor bars - each "half shirt" above the health bar
        double absorbRatio = getEnergyStoredPercent(armor);
        //Make sure that the absorb ratio is always at least the minimum protect if there is energy left
        //but also that it is never above the max
        if (getEnergyStored(armor) != 0) {
            absorbRatio += minProtect;
        }
        if (absorbRatio > maxProtect) {
            absorbRatio = maxProtect;
        }
        //Multiply by 25 to get amount/25
        //Divide by 4 (multiply by .25) to get amount per armor piece
        double armorBars = absorbRatio * 25 * .25;
        //Make sure that SOME bars are displayed as long as the protection is greater than 0
        if (armorBars > 0 && armorBars < 1) {
            armorBars = 1;
        }
        return MathHelper.floor(armorBars);
    }

    @Override
    public void damageArmor(EntityLivingBase entity, ItemStack stack,
            DamageSource source, int damage, int slot) {
        extractEnergy(stack, energyPerHit, false);
    }
  
    //TODO - POWER BAR
}
To be more exact - when I wear the power armor, one of two things happens. If it has any amount of charge, I take zero damage from all blockable sources (mobs, cacti, lava, etc.). When it has zero charge, I take full damage from all sources, including blockable ones (the second part - full damage at zero charge - is intended). All other bits of code for the armor are working, including the dynamic armor bar change (which I find charming) and the charging.

[Reply to side questions in spoilers so the moderators don't eat me ^^]
Side Question - How would I specify "CoFH Core OR CoFH Lib" as a requirement in my main mod code? I understand hard and soft dependencies, but don't know how to do an "either or" sort of thing. :p
Second Side Question - How would I do the power bar? :p I'll figure it out myself later if I need to.

Thanks! :D
 
Last edited:

lenscas

Over-Achiever
Jul 31, 2013
2,015
1,799
248
Side Question - How would I specify "CoFH Core OR CoFH Lib" as a requirement in my main mod code? I understand hard and soft dependencies, but don't know how to do an "either or" sort of thing. :p

not written any mods yet or touched java so what I suggest is probably not the way to do it:

Put them both as a soft dependency and look by yourself if either of them is loaded if neither of them is loaded then you can probably crash the game in a nice way.

Again, I have not done anything with java let alone mod minecraft so their may be a better way to do it.
 
  • Like
Reactions: Type1Ninja

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
not written any mods yet or touched java so what I suggest is probably not the way to do it:

Put them both as a soft dependency and look by yourself if either of them is loaded if neither of them is loaded then you can probably crash the game in a nice way.

Again, I have not done anything with java let alone mod minecraft so their may be a better way to do it.
I know there's a lot of mods - like Matter Overdrive - that have that sort of optional dependency (working properly, I think) so I'll probably post over on that thread if nobody responds here. It might be that yours is the ONLY method, although I'm hoping there's something cleaner. :p Thanks for the guess, though. :)
 

lenscas

Over-Achiever
Jul 31, 2013
2,015
1,799
248
I know there's a lot of mods - like Matter Overdrive - that have that sort of optional dependency (working properly, I think) so I'll probably post over on that thread if nobody responds here. It might be that yours is the ONLY method, although I'm hoping there's something cleaner. :p Thanks for the guess, though. :)

To be honest if there is not a cleaner way to do it then I would be surprised.
 
  • Like
Reactions: Type1Ninja

gattsuru

Well-Known Member
May 25, 2013
364
103
68
I assume the logged absorbratio is showing a sane (0.1-0.75) range during the call? May I ask if you run into similar issues if you switch "return new ArmorProperties(1, absorbRatio, Integer.MAX_VALUE);" with something more like "return new ArmorProperties(1, absorbRatio, 100);" , just in case MaxAbsorb does odd things with high values?

I don't think the required-after syntax supports conditional requirements, so you're probably better off putting both as after dependencies and checking for the existence of their classes during your own loading stage.
 
  • Like
Reactions: Type1Ninja

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
I assume the logged absorbratio is showing a sane (0.1-0.75) range during the call? May I ask if you run into similar issues if you switch "return new ArmorProperties(1, absorbRatio, Integer.MAX_VALUE);" with something more like "return new ArmorProperties(1, absorbRatio, 100);" , just in case MaxAbsorb does odd things with high values?

I don't think the required-after syntax supports conditional requirements, so you're probably better off putting both as after dependencies and checking for the existence of their classes during your own loading stage.
Yeah, it does display sane values (I'm glad you were able to figure out that those were the max and min from my code ;)). I'll try playing with the last parameter next time I work on that. Thanks for the advice! :D
Also thanks for the dependency stuff. :)
 

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
I assume the logged absorbratio is showing a sane (0.1-0.75) range during the call? May I ask if you run into similar issues if you switch "return new ArmorProperties(1, absorbRatio, Integer.MAX_VALUE);" with something more like "return new ArmorProperties(1, absorbRatio, 100);" , just in case MaxAbsorb does odd things with high values?

I don't think the required-after syntax supports conditional requirements, so you're probably better off putting both as after dependencies and checking for the existence of their classes during your own loading stage.
Alright, I did my test. Setting the max damage absorption (third parameter) to 10, I take full-ish damage from a zombie pigman. Setting it to 100, I don't take any damage until I've reached about 2,000/10,000 RF (maybe lower). The amount of damage taken goes up slightly, until it finally reaches full damage at 0 charge. I'm tired right now and don't know what to make of that... My code in the working Hazmat Suit appears to work fine, and I can't tell what's different about the Powered Armor. :(
 

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
So, I fixed the issue. For all those who are curious, I forgot that the absorb ratio is per piece of armor. Simply dividing absorbRatio by four and THEN passing it to the ArmorProperties instance solved the issue. That was a real headache... :(
 
C

chbachman

Guest
If you don't mind me asking, what are you hoping to do with your mod?

I currently develop Modular Armour, which seems to be pretty close (at least with the armour section) to what you are doing.

If you ever need help, I have figured out most of this through trial and error.

My github repository is here, for some code examples.

I am available here, with a private message or post in this forum, or on EsperNet IRC, in the channel ModularArmour.
 

Type1Ninja

New Member
Jul 29, 2019
1,393
-7
0
If you don't mind me asking, what are you hoping to do with your mod?

I currently develop Modular Armour, which seems to be pretty close (at least with the armour section) to what you are doing.

If you ever need help, I have figured out most of this through trial and error.

My github repository is here, for some code examples.

I am available here, with a private message or post in this forum, or on EsperNet IRC, in the channel ModularArmour.
Just a quick summary of what I'm trying to do:
I want a mod that adds everything I haven't seen in other mods done in the way I want it to be done. That includes a lightning creator, various specialized suits of armor, and an Enderman-style RF-powered handheld teleporter, as well as some other stuff. I'll be sure to look at your github for help. :D
I may also look at your mod for my modpack. :)
 

King Lemming

New Member
Jul 29, 2019
664
0
0
SEE LAST POST FOR SOLUTION
Side Question - How would I specify "CoFH Core OR CoFH Lib" as a requirement in my main mod code? I understand hard and soft dependencies, but don't know how to do an "either or" sort of thing. :p
Second Side Question - How would I do the power bar? :p I'll figure it out myself later if I need to.
Thanks! :D

Just specify CoFHLib. It ships with Core, but it still counts as being there.
Also, https://github.com/CoFH/RedstoneArs...h/redstonearsenal/item/armor/ItemArmorRF.java is how armor is done, in general.