From 710d15a66cf0fc51e1dd0781386d3d72307b8efc Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Fri, 29 Jul 2016 01:04:07 -0700 Subject: [PATCH 1/6] Add files via upload New Reactive Armor Hardener code that should work correctly. --- eos/effects/adaptivearmorhardener.py | 100 ++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index 1202b1fde..323bbdeac 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -2,17 +2,97 @@ # # Used by: # Module: Reactive Armor Hardener +import logging + +logger = logging.getLogger(__name__) + +runTime = "late" type = "active" def handler(fit, module, context): - for type in ("kinetic", "thermal", "explosive", "em"): - attr = "armor%sDamageResonance" % type.capitalize() + damagePattern = fit.damagePattern - #Adjust RAH to match the current damage pattern - damagePattern = fit.damagePattern - if damagePattern: - attrDamagePattern = "%sAmount" % type - damagePatternModifier = getattr(damagePattern,attrDamagePattern)/float(sum((damagePattern.emAmount,damagePattern.thermalAmount,damagePattern.kineticAmount,damagePattern.explosiveAmount))) - modifiedResistModifier = (1-(((1-module.getModifiedItemAttr(attr))*4)*(damagePatternModifier))) - module.forceItemAttr(attr, modifiedResistModifier) + # Skip if there is no damage pattern. Example: projected ships or fleet boosters + if damagePattern: + logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) + logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) - fit.ship.multiplyItemAttr(attr, module.getModifiedItemAttr(attr), stackingPenalties=True, penaltyGroup="preMul") + # Populate a tuple with the damage profile modified by current armor resists. + baseDamageTaken = ( + damagePattern.emAmount * fit.ship.getModifiedItemAttr('armorEmDamageResonance'), + damagePattern.thermalAmount * fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), + damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), + damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), + ) + logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + + resistanceShiftAmount = module.getModifiedItemAttr('resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction + RAHResistance = [ + module.itemModifiedAttributes.getOriginal('armorEmDamageResonance'), + module.itemModifiedAttributes.getOriginal('armorThermalDamageResonance'), + module.itemModifiedAttributes.getOriginal('armorKineticDamageResonance'), + module.itemModifiedAttributes.getOriginal('armorExplosiveDamageResonance'), + ] + + # Simulate RAH cycles until the RAH either stops changing or enters a loop. + # The number of iterations is limited to prevent an infinite loop if something goes wrong. + cycleList = [] + loopStart = -1 + for num in range(1, 51): + cycleList.append(list(RAHResistance)) + + logger.debug("Starting cycle %d.") + # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. + damagePattern_tuples = [ + (0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]), + (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), + (2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]), + (1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]), + ] + logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) + + # Sort the tuple to drop the highest damage value to the bottom + sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1]) + + if sortedDamagePattern_tuples[2][1] == 0: + # One damage type: the top damage type takes from the other three + change0 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[0][2]) + change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) + change2 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[2][2]) + change3 = -(change0 + change1 + change2) + else: + # Two or more damage types: the top two damage types take from the other two + change0 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[0][2]) + change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) + change2 = -(change0 + change1) / 2 + change3 = -(change0 + change1) / 2 + + RAHResistance[sortedDamagePattern_tuples[0][0]] = sortedDamagePattern_tuples[0][2] + change0 + RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 + RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 + RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 + logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + + # See if the current RAH profile has been encountered before, indicating a loop. + for i, val in enumerate(cycleList): + tolerance = 1e-09 + if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: + loopStart = i + logger.debug("Loop found: %d-%d", loopStart, num) + break + if loopStart >= 0: break + if loopStart >= 0: break + + # Average the RAH profiles that it loops through + loopCycles = cycleList[loopStart:] + average = [0, 0, 0, 0] + for cycle in loopCycles: + for i in range(4): + average[i] += cycle[i] / len(loopCycles) + + # Set the new resistances + logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + for i, damagePatternType in enumerate(('Em', 'Thermal', 'Kinetic', 'Explosive')): + attr = "armor%sDamageResonance" % damagePatternType + module.forceItemAttr(attr, average[i]) + fit.ship.multiplyItemAttr(attr, module.getModifiedItemAttr(attr), stackingPenalties=True, penaltyGroup="preMul") + \ No newline at end of file From f895e82c699bf2decb0477287ac14fc431c7f95a Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Fri, 29 Jul 2016 22:44:38 -0700 Subject: [PATCH 2/6] Commented out logging and made a couple tweaks --- eos/effects/adaptivearmorhardener.py | 36 +++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index 323bbdeac..e06d1a9c9 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -13,8 +13,8 @@ def handler(fit, module, context): # Skip if there is no damage pattern. Example: projected ships or fleet boosters if damagePattern: - logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) - logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) + #logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) + #logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) # Populate a tuple with the damage profile modified by current armor resists. baseDamageTaken = ( @@ -23,24 +23,22 @@ def handler(fit, module, context): damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), ) - logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + #logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) resistanceShiftAmount = module.getModifiedItemAttr('resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction RAHResistance = [ - module.itemModifiedAttributes.getOriginal('armorEmDamageResonance'), - module.itemModifiedAttributes.getOriginal('armorThermalDamageResonance'), - module.itemModifiedAttributes.getOriginal('armorKineticDamageResonance'), - module.itemModifiedAttributes.getOriginal('armorExplosiveDamageResonance'), + module.getModifiedItemAttr('armorEmDamageResonance'), + module.getModifiedItemAttr('armorThermalDamageResonance'), + module.getModifiedItemAttr('armorKineticDamageResonance'), + module.getModifiedItemAttr('armorExplosiveDamageResonance'), ] # Simulate RAH cycles until the RAH either stops changing or enters a loop. # The number of iterations is limited to prevent an infinite loop if something goes wrong. cycleList = [] loopStart = -1 - for num in range(1, 51): - cycleList.append(list(RAHResistance)) - - logger.debug("Starting cycle %d.") + for num in range(50): + #logger.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. damagePattern_tuples = [ (0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]), @@ -48,7 +46,7 @@ def handler(fit, module, context): (2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]), (1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]), ] - logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) + #logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) # Sort the tuple to drop the highest damage value to the bottom sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1]) @@ -70,27 +68,33 @@ def handler(fit, module, context): RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 - logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + #logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): tolerance = 1e-09 if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - logger.debug("Loop found: %d-%d", loopStart, num) + #logger.debug("Loop found: %d-%d", loopStart + 1, num) break if loopStart >= 0: break if loopStart >= 0: break + + cycleList.append(list(RAHResistance)) + + if loopStart < 0: + logger.error("Reactive Armor Hardener failed to find equalibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) # Average the RAH profiles that it loops through loopCycles = cycleList[loopStart:] + numCycles = len(loopCycles) average = [0, 0, 0, 0] for cycle in loopCycles: for i in range(4): - average[i] += cycle[i] / len(loopCycles) + average[i] += cycle[i] / numCycles # Set the new resistances - logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) for i, damagePatternType in enumerate(('Em', 'Thermal', 'Kinetic', 'Explosive')): attr = "armor%sDamageResonance" % damagePatternType module.forceItemAttr(attr, average[i]) From fea1a2876035bbc905e0993755dcecc42db9d847 Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Fri, 29 Jul 2016 22:49:34 -0700 Subject: [PATCH 3/6] Grr, tab characters, grr --- eos/effects/adaptivearmorhardener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index e06d1a9c9..821c978d7 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -87,7 +87,7 @@ def handler(fit, module, context): # Average the RAH profiles that it loops through loopCycles = cycleList[loopStart:] - numCycles = len(loopCycles) + numCycles = len(loopCycles) average = [0, 0, 0, 0] for cycle in loopCycles: for i in range(4): From 394381c7363c3137b0b28dd4b7b3344e1ecdcb1a Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Sat, 30 Jul 2016 04:20:00 -0700 Subject: [PATCH 4/6] I think I changed something... This still works the same, but I think I changed a couple things that didn't look right. --- eos/effects/adaptivearmorhardener.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index 821c978d7..f7ba97ce1 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -40,6 +40,7 @@ def handler(fit, module, context): for num in range(50): #logger.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. + # This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result. damagePattern_tuples = [ (0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]), (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), @@ -53,12 +54,20 @@ def handler(fit, module, context): if sortedDamagePattern_tuples[2][1] == 0: # One damage type: the top damage type takes from the other three - change0 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[0][2]) - change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) - change2 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[2][2]) + # Since the resistances not taking damage will end up going to the type taking damage we just do the whole thing at once. + change0 = 1 - sortedDamagePattern_tuples[0][2] + change1 = 1 - sortedDamagePattern_tuples[1][2] + change2 = 1 - sortedDamagePattern_tuples[2][2] change3 = -(change0 + change1 + change2) + elif sortedDamagePattern_tuples[1][1] == 0: + # Two damage types: the top two damage types take from the other two + # Since the resistances not taking damage will end up going equally to the types taking damage we just do the whole thing at once. + change0 = 1 - sortedDamagePattern_tuples[0][2] + change1 = 1 - sortedDamagePattern_tuples[1][2] + change2 = -(change0 + change1) / 2 + change3 = -(change0 + change1) / 2 else: - # Two or more damage types: the top two damage types take from the other two + # Three or four damage types: the top two damage types take from the other two change0 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[0][2]) change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) change2 = -(change0 + change1) / 2 @@ -75,7 +84,7 @@ def handler(fit, module, context): tolerance = 1e-09 if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - #logger.debug("Loop found: %d-%d", loopStart + 1, num) + #logger.debug("Loop found: %d-%d", loopStart, num) break if loopStart >= 0: break if loopStart >= 0: break @@ -83,7 +92,7 @@ def handler(fit, module, context): cycleList.append(list(RAHResistance)) if loopStart < 0: - logger.error("Reactive Armor Hardener failed to find equalibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) # Average the RAH profiles that it loops through loopCycles = cycleList[loopStart:] @@ -95,8 +104,7 @@ def handler(fit, module, context): # Set the new resistances #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) - for i, damagePatternType in enumerate(('Em', 'Thermal', 'Kinetic', 'Explosive')): - attr = "armor%sDamageResonance" % damagePatternType + for i, attr in enumerate(('armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): module.forceItemAttr(attr, average[i]) - fit.ship.multiplyItemAttr(attr, module.getModifiedItemAttr(attr), stackingPenalties=True, penaltyGroup="preMul") + fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul") \ No newline at end of file From c700d9b661fa8332abfb612698381bfe278ac94f Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Sun, 18 Sep 2016 05:52:50 -0700 Subject: [PATCH 5/6] Adjusted the tolerance for finding loops and rounded the result --- eos/effects/adaptivearmorhardener.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index f7ba97ce1..a75a1e280 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -36,7 +36,7 @@ def handler(fit, module, context): # Simulate RAH cycles until the RAH either stops changing or enters a loop. # The number of iterations is limited to prevent an infinite loop if something goes wrong. cycleList = [] - loopStart = -1 + loopStart = -20 for num in range(50): #logger.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. @@ -81,12 +81,11 @@ def handler(fit, module, context): # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): - tolerance = 1e-09 + tolerance = 1e-06 if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i #logger.debug("Loop found: %d-%d", loopStart, num) break - if loopStart >= 0: break if loopStart >= 0: break cycleList.append(list(RAHResistance)) @@ -94,13 +93,16 @@ def handler(fit, module, context): if loopStart < 0: logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) - # Average the RAH profiles that it loops through + # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. loopCycles = cycleList[loopStart:] numCycles = len(loopCycles) average = [0, 0, 0, 0] for cycle in loopCycles: for i in range(4): - average[i] += cycle[i] / numCycles + average[i] += cycle[i] + + for i in range(4): + average[i] = round(average[i] / numCycles, 3) # Set the new resistances #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) From 7de90cc2d869139b1a0799347a243e8c1f2cd761 Mon Sep 17 00:00:00 2001 From: "Mr. Nukealizer" Date: Mon, 19 Sep 2016 02:50:00 -0700 Subject: [PATCH 6/6] Changed forceItemAttr to increaseItemAttr --- eos/effects/adaptivearmorhardener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index a75a1e280..bf20478c2 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -107,6 +107,6 @@ def handler(fit, module, context): # Set the new resistances #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) for i, attr in enumerate(('armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): - module.forceItemAttr(attr, average[i]) + module.increaseItemAttr(attr, average[i] - module.getModifiedItemAttr(attr)) fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul") \ No newline at end of file