diff --git a/eos/capSim.py b/eos/capSim.py index 9b8ffc305..8b16c3e0f 100644 --- a/eos/capSim.py +++ b/eos/capSim.py @@ -72,7 +72,7 @@ class CapSimulator(object): disable_period = False # Loop over modules, clearing clipSize if applicable, and group modules based on attributes - for (duration, capNeed, clipSize, disableStagger) in self.modules: + for (duration, capNeed, clipSize, disableStagger, reloadTime) in self.modules: if self.scale: duration, capNeed = self.scale_activation(duration, capNeed) @@ -80,24 +80,25 @@ class CapSimulator(object): # a cap booster module. if not self.reload and capNeed > 0: clipSize = 0 + reloadTime = 0 # Group modules based on their properties - if (duration, capNeed, clipSize, disableStagger) in mods: - mods[(duration, capNeed, clipSize, disableStagger)] += 1 + if (duration, capNeed, clipSize, disableStagger, reloadTime) in mods: + mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] += 1 else: - mods[(duration, capNeed, clipSize, disableStagger)] = 1 + mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1 # Loop over grouped modules, configure staggering and push to the simulation state - for (duration, capNeed, clipSize, disableStagger), amount in mods.items(): + for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items(): if self.stagger and not disableStagger: if clipSize == 0: duration = int(duration / amount) else: - stagger_amount = (duration * clipSize + 10000) / (amount * clipSize) + stagger_amount = (duration * clipSize + reloadTime) / (amount * clipSize) for i in range(1, amount): heapq.heappush(self.state, [i * stagger_amount, duration, - capNeed, 0, clipSize]) + capNeed, 0, clipSize, reloadTime]) else: capNeed *= amount @@ -107,7 +108,7 @@ class CapSimulator(object): if clipSize: disable_period = True - heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize]) + heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime]) if disable_period: self.period = self.t_max @@ -144,7 +145,7 @@ class CapSimulator(object): while 1: activation = pop(state) - t_now, duration, capNeed, shot, clipSize = activation + t_now, duration, capNeed, shot, clipSize, reloadTime = activation if t_now >= t_max: break @@ -180,7 +181,7 @@ class CapSimulator(object): if clipSize: if shot % clipSize == 0: shot = 0 - t_now += 10000 # include reload time + t_now += reloadTime # include reload time activation[0] = t_now activation[3] = shot diff --git a/eos/effects/doomsdaybeamdot.py b/eos/effects/doomsdaybeamdot.py new file mode 100644 index 000000000..fff941579 --- /dev/null +++ b/eos/effects/doomsdaybeamdot.py @@ -0,0 +1,9 @@ +# doomsdayBeamDOT +# +# Used by: +# Module: Lance type modules +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/doomsdayconedot.py b/eos/effects/doomsdayconedot.py new file mode 100644 index 000000000..3e92fb8f6 --- /dev/null +++ b/eos/effects/doomsdayconedot.py @@ -0,0 +1,9 @@ +# doomsdayConeDOT +# +# Used by: +# Module: Bosonic Field Generator +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/doomsdayhog.py b/eos/effects/doomsdayhog.py new file mode 100644 index 000000000..182d0c29f --- /dev/null +++ b/eos/effects/doomsdayhog.py @@ -0,0 +1,9 @@ +# doomsdayHOG +# +# Used by: +# Module: GTFO - Gravitational Transportation Field Oscillator +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/doomsdayslash.py b/eos/effects/doomsdayslash.py new file mode 100644 index 000000000..e6b55ede9 --- /dev/null +++ b/eos/effects/doomsdayslash.py @@ -0,0 +1,9 @@ +# doomsdaySlash +# +# Used by: +# Module: Reaper type modules +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/microjumpportaldrive.py b/eos/effects/microjumpportaldrive.py new file mode 100644 index 000000000..40925b491 --- /dev/null +++ b/eos/effects/microjumpportaldrive.py @@ -0,0 +1,9 @@ +# microJumpPortalDrive +# +# Used by: +# Module: MJFG - Micro Jump Field Generator (used on command destroyers) +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py index f22439441..aee7b22f9 100644 --- a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py @@ -11,8 +11,7 @@ def handler(fit, module, context): fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength")) if module.charge is not None and module.charge.ID == 47336: for mod in fit.modules: - if not mod.isEmpty and mod.state > State.ONLINE and ( - mod.item.requiresSkill("High Speed Maneuvering") - or mod.item.requiresSkill("Micro Jump Drive Operation") - ): + if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE: + mod.state = State.ONLINE + if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE: mod.state = State.ONLINE diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index d77c56e3e..57a58372b 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -15,12 +15,13 @@ def handler(fit, src, context): if src.item.group.name == 'Missile Launcher Bomb': # Bomb Launcher Cooldown Timer moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") + speed = src.getModifiedItemAttr("speed") # Void and Focused Void Bombs neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount") - if moduleReactivationDelay and neutAmount: - fit.addDrain(src, moduleReactivationDelay, neutAmount, 0) + if moduleReactivationDelay and neutAmount and speed: + fit.addDrain(src, speed + moduleReactivationDelay, neutAmount, 0) # Lockbreaker Bombs ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType)) diff --git a/eos/effects/warpdisruptsphere.py b/eos/effects/warpdisruptsphere.py index 7cddfcb86..ef640f5ff 100644 --- a/eos/effects/warpdisruptsphere.py +++ b/eos/effects/warpdisruptsphere.py @@ -1,4 +1,3 @@ - # warpDisruptSphere # # Used by: @@ -11,7 +10,6 @@ runTime = "early" def handler(fit, module, context): - if "projected" in context: fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength")) if module.charge is not None and module.charge.ID == 45010: diff --git a/eos/effects/warpscrambleblockmwdwithnpceffect.py b/eos/effects/warpscrambleblockmwdwithnpceffect.py index 2e075207e..89c97117e 100644 --- a/eos/effects/warpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/warpscrambleblockmwdwithnpceffect.py @@ -21,3 +21,5 @@ def handler(fit, module, context): or mod.item.requiresSkill("High Speed Maneuvering") ): mod.state = State.ONLINE + if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE: + mod.state = State.ONLINE diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 7c905c028..f222f3a17 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -121,8 +121,12 @@ class Character(object): def apiUpdateCharSheet(self, skills, secStatus=0): for skillRow in skills: - skill = self.getSkill(int(skillRow["typeID"])) - skill.setLevel(int(skillRow["level"]), persist=True, ignoreRestrict=True) + try: + skill = self.getSkill(int(skillRow["typeID"])) + skill.setLevel(int(skillRow["level"]), persist=True, ignoreRestrict=True) + except: + # if setting a skill doesn't work, it's not the end of the world, just quietly pass + pass self.secStatus = secStatus diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index e453bc7d0..45ad6171d 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1084,7 +1084,7 @@ class Fit(object): def calculateSustainableTank(self, effective=True): if self.__sustainableTank is None: - if self.capStable: + if self.capStable and not self.factorReload: sustainable = { "armorRepair" : self.extraAttributes["armorRepair"], "shieldRepair": self.extraAttributes["shieldRepair"], @@ -1142,16 +1142,34 @@ class Fit(object): usesCap = False except AttributeError: usesCap = False - # Modules which do not use cap are not penalized based on cap use - if usesCap: - cycleTime = mod.getModifiedItemAttr("duration") - amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + + cycleTime = mod.rawCycleTime + amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + # Normal Repairers + if usesCap and not mod.charge: sustainable[attr] -= amount / (cycleTime / 1000.0) repairers.append(mod) + # Ancillary Armor reps etc + elif usesCap and mod.charge: + if mod.charge.name == "Nanite Repair Paste": + multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 + else: + multiplier = 1 + sustainable[attr] -= amount * multiplier / (cycleTime / 1000.0) + repairers.append(mod) + # Ancillary Shield boosters etc + elif not usesCap: + if self.factorReload and mod.charge: + reloadtime = mod.reloadTime + else: + reloadtime = 0.0 + offdutycycle = reloadtime / ((max(mod.numShots, 1) * cycleTime) + reloadtime) + sustainable[attr] -= amount * offdutycycle / (cycleTime / 1000.0) # Sort repairers by efficiency. We want to use the most efficient repairers first repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr( - groupAttrMap[_mod.item.group.name]) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) + groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr( + "chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) # Loop through every module until we're above peak recharge # Most efficient first, as we sorted earlier. @@ -1160,15 +1178,35 @@ class Fit(object): for mod in repairers: if capUsed > totalPeakRecharge: break - cycleTime = mod.cycleTime + + if self.factorReload and mod.charge: + reloadtime = mod.reloadTime + else: + reloadtime = 0.0 + + cycleTime = mod.rawCycleTime capPerSec = mod.capUse + if capPerSec is not None and cycleTime is not None: # Check how much this repper can work sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec) - - # Add the sustainable amount amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) - sustainable[groupStoreMap[mod.item.group.name]] += sustainability * (amount / (cycleTime / 1000.0)) + # Add the sustainable amount + + if not mod.charge: + sustainable[groupStoreMap[mod.item.group.name]] += sustainability * amount / ( + cycleTime / 1000.0) + else: + if mod.charge.name == "Nanite Repair Paste": + multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 + else: + multiplier = 1 + ondutycycle = (max(mod.numShots, 1) * cycleTime) / ( + (max(mod.numShots, 1) * cycleTime) + reloadtime) + sustainable[groupStoreMap[ + mod.item.group.name]] += sustainability * amount * ondutycycle * multiplier / ( + cycleTime / 1000.0) + capUsed += capPerSec sustainable["passiveShield"] = self.calculateShieldRecharge() @@ -1186,7 +1224,7 @@ class Fit(object): rechargeRate = self.ship.getModifiedItemAttr("shieldRechargeRate") / 1000.0 return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity - def addDrain(self, src, cycleTime, capNeed, clipSize=0): + def addDrain(self, src, cycleTime, capNeed, clipSize=0, reloadTime=0): """ Used for both cap drains and cap fills (fills have negative capNeed) """ energyNeutralizerSignatureResolution = src.getModifiedItemAttr("energyNeutralizerSignatureResolution") @@ -1196,7 +1234,7 @@ class Fit(object): if energyNeutralizerSignatureResolution: capNeed = capNeed * min(1, signatureRadius / energyNeutralizerSignatureResolution) - self.__extraDrains.append((cycleTime, capNeed, clipSize)) + self.__extraDrains.append((cycleTime, capNeed, clipSize, reloadTime)) def removeDrain(self, i): del self.__extraDrains[i] @@ -1214,6 +1252,7 @@ class Fit(object): cycleTime = mod.rawCycleTime or 0 reactivationTime = mod.getModifiedItemAttr("moduleReactivationDelay") or 0 fullCycleTime = cycleTime + reactivationTime + reloadTime = mod.reloadTime if fullCycleTime > 0: capNeed = mod.capUse if capNeed > 0: @@ -1225,11 +1264,11 @@ class Fit(object): disableStagger = mod.hardpoint == Hardpoint.TURRET drains.append((int(fullCycleTime), mod.getModifiedItemAttr("capacitorNeed") or 0, - mod.numShots or 0, disableStagger)) + mod.numShots or 0, disableStagger, reloadTime)) - for fullCycleTime, capNeed, clipSize in self.iterDrains(): + for fullCycleTime, capNeed, clipSize, reloadTime in self.iterDrains(): # Stagger incoming effects for cap simulation - drains.append((int(fullCycleTime), capNeed, clipSize, False)) + drains.append((int(fullCycleTime), capNeed, clipSize, False, reloadTime)) if capNeed > 0: capUsed += capNeed / (fullCycleTime / 1000.0) else: diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 3c5dc34bb..054828dae 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -337,8 +337,15 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): volley *= self.getModifiedItemAttr("damageMultiplier") or 1 if volley: cycleTime = self.cycleTime + # Some weapons repeat multiple times in one cycle (think doomsdays) + # Get the number of times it fires off + weaponDoT = max( + self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), + 1 + ) + self.__volley = volley - self.__dps = volley / (cycleTime / 1000.0) + self.__dps = (volley * weaponDoT) / (cycleTime / 1000.0) return self.__dps, self.__volley @@ -710,7 +717,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): # Module can only fire one shot at a time, think bomb launchers or defender launchers if self.disallowRepeatingAction: - if numShots > 1: + if numShots > 0: """ The actual mechanics behind this is complex. Behavior will be (for 3 ammo): fire, reactivation delay, fire, reactivation delay, fire, max(reactivation delay, reload) @@ -720,12 +727,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): Currently would apply to bomb launchers and defender missiles """ - effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0)) / numShots + effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0)) else: """ Applies to MJD/MJFG """ effective_reload_time = max(raw_reload_time, self.reactivationDelay, 0) + speed = speed + effective_reload_time else: """ Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it. diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index 6484595eb..ed23e606a 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -72,6 +72,7 @@ class AmountChanger(wx.Dialog): self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) self.input.SetValue(str(value)) + self.input.SelectAll() bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) @@ -82,7 +83,6 @@ class AmountChanger(wx.Dialog): bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) self.input.SetFocus() - self.input.SetInsertionPointEnd() self.input.Bind(wx.EVT_CHAR, self.onChar) self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) self.SetSizer(bSizer1) diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 5378482ad..668979c1f 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -76,6 +76,31 @@ class Miscellanea(ViewColumn): stuff.getModifiedItemAttr("boosterDuration") text = "{0} min".format(formatAmount(stuff.getModifiedItemAttr("boosterDuration") / 1000 / 60, 3, 0, 3)) return text, "Booster Duration" + elif itemGroup in ("Super Weapon", "Structure Doomsday Weapon"): + doomsday_duration = stuff.getModifiedItemAttr("doomsdayDamageDuration", 1) + doomsday_dottime = stuff.getModifiedItemAttr("doomsdayDamageCycleTime", 1) + func = stuff.getModifiedItemAttr + + volley = sum( + map( + lambda attr: (func("%sDamage" % attr) or 0), + ("em", "thermal", "kinetic", "explosive") + ) + ) + volley *= stuff.getModifiedItemAttr("damageMultiplier") or 1 + + if volley <= 0: + text = "" + tooltip = "" + elif max(doomsday_duration / doomsday_dottime, 1) > 1: + text = "{0} dmg over {1} s".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3), doomsday_duration / 1000) + tooltip = "Raw damage done over time" + else: + text = "{0} dmg".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3)) + tooltip = "Raw damage done" + return text, tooltip + + pass elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") if not trackingSpeed: