Files
pyfa/effs_stat_export.py
2018-05-27 04:07:21 -04:00

447 lines
22 KiB
Python
Executable File

import inspect
import os
import platform
import re
import sys
import traceback
from math import log
import eos.db
eos.db.saveddata_meta.create_all()
import json
from service.fit import Fit
def attrDirectMap(values, target, source):
for val in values:
target[val] = source.itemModifiedAttributes[val]
def getT2MwdSpeed(fit, fitL):
fitID = fit.ID
propID = None
rigSize = fit.ship.itemModifiedAttributes['rigSize']
if rigSize == 1 and fit.ship.itemModifiedAttributes['medSlots'] > 0:
propID = 440
elif rigSize == 2 and fit.ship.itemModifiedAttributes['medSlots'] > 0:
propID = 12076
elif rigSize == 3 and fit.ship.itemModifiedAttributes['medSlots'] > 0:
propID = 12084
elif rigSize == 4 and fit.ship.itemModifiedAttributes['medSlots'] > 0:
if fit.ship.itemModifiedAttributes['powerOutput'] > 60000:
propID = 41253
else:
propID = 12084
elif rigSize == None and fit.ship.itemModifiedAttributes['medSlots'] > 0:
propID = 440
if propID:
fitL.appendModule(fitID, propID)
fitL.recalc(fit)
fit = eos.db.getFit(fitID)
mwdPropSpeed = fit.maxSpeed
mwdPosition = list(filter(lambda mod: mod.item and mod.item.ID == propID, fit.modules))[0].position
fitL.removeModule(fitID, mwdPosition)
fitL.recalc(fit)
fit = eos.db.getFit(fitID)
return mwdPropSpeed
def getPropData(fit, fitL):
fitID = fit.ID
propMods = list(filter(lambda mod: mod.item and mod.item.groupID in [46], fit.modules))
possibleMWD = list(filter(lambda mod: 'signatureRadiusBonus' in mod.item.attributes, propMods))
if len(possibleMWD) > 0 and possibleMWD[0].state > 0:
mwd = possibleMWD[0]
oldMwdState = mwd.state
mwd.state = 0
fitL.recalc(fit)
fit = eos.db.getFit(fitID)
sp = fit.maxSpeed
sig = fit.ship.itemModifiedAttributes['signatureRadius']
mwd.state = oldMwdState
fitL.recalc(fit)
fit = eos.db.getFit(fitID)
return {'usingMWD': True, 'unpropedSpeed': sp, 'unpropedSig': sig}
return {'usingMWD': False, 'unpropedSpeed': fit.maxSpeed, 'unpropedSig': fit.ship.itemModifiedAttributes['signatureRadius']}
def getOutgoingProjectionData(fit):
# This is a subset of module groups capable of projection and a superset of those currently used by efs
projectedModGroupIds = [
41, 52, 65, 67, 68, 71, 80, 201, 208, 291, 325, 379, 585,
842, 899, 1150, 1154, 1189, 1306, 1672, 1697, 1698, 1815, 1894
]
projectedMods = list(filter(lambda mod: mod.item and mod.item.groupID in projectedModGroupIds, fit.modules))
projections = []
for mod in projectedMods:
stats = {}
if mod.item.groupID == 65 or mod.item.groupID == 1672:
stats['type'] = 'Stasis Web'
stats['optimal'] = mod.itemModifiedAttributes['maxRange']
attrDirectMap(['duration', 'speedFactor'], stats, mod)
elif mod.item.groupID == 291:
stats['type'] = 'Weapon Disruptor'
stats['optimal'] = mod.itemModifiedAttributes['maxRange']
stats['falloff'] = mod.itemModifiedAttributes['falloffEffectiveness']
attrDirectMap([
'trackingSpeedBonus', 'maxRangeBonus', 'falloffBonus', 'aoeCloudSizeBonus',\
'aoeVelocityBonus', 'missileVelocityBonus', 'explosionDelayBonus'\
], stats, mod)
elif mod.item.groupID == 68:
stats['type'] = 'Energy Nosferatu'
attrDirectMap(['powerTransferAmount', 'energyNeutralizerSignatureResolution'], stats, mod)
elif mod.item.groupID == 71:
stats['type'] = 'Energy Neutralizer'
attrDirectMap([
'energyNeutralizerSignatureResolution','entityCapacitorLevelModifierSmall',\
'entityCapacitorLevelModifierMedium', 'entityCapacitorLevelModifierLarge',\
'energyNeutralizerAmount'\
], stats, mod)
elif mod.item.groupID == 41 or mod.item.groupID == 1697:
stats['type'] = 'Remote Shield Booster'
attrDirectMap(['shieldBonus'], stats, mod)
elif mod.item.groupID == 325 or mod.item.groupID == 1698:
stats['type'] = 'Remote Armor Repairer'
attrDirectMap(['armorDamageAmount'], stats, mod)
elif mod.item.groupID == 52:
stats['type'] = 'Warp Scrambler'
attrDirectMap(['activationBlockedStrenght', 'warpScrambleStrength'], stats, mod)
elif mod.item.groupID == 379:
stats['type'] = 'Target Painter'
attrDirectMap(['signatureRadiusBonus'], stats, mod)
elif mod.item.groupID == 208:
stats['type'] = 'Sensor Dampener'
attrDirectMap(['maxTargetRangeBonus', 'scanResolutionBonus'], stats, mod)
elif mod.item.groupID == 201:
stats['type'] = 'ECM'
attrDirectMap([
'scanGravimetricStrengthBonus', 'scanMagnetometricStrengthBonus',\
'scanRadarStrengthBonus', 'scanLadarStrengthBonus',\
], stats, mod)
elif mod.item.groupID == 80:
stats['type'] = 'Burst Jammer'
mod.itemModifiedAttributes['maxRange'] = mod.itemModifiedAttributes['ecmBurstRange']
attrDirectMap([
'scanGravimetricStrengthBonus', 'scanMagnetometricStrengthBonus',\
'scanRadarStrengthBonus', 'scanLadarStrengthBonus',\
], stats, mod)
elif mod.item.groupID == 1189:
stats['type'] = 'Micro Jump Drive'
mod.itemModifiedAttributes['maxRange'] = 0
attrDirectMap(['moduleReactivationDelay'], stats, mod)
if mod.itemModifiedAttributes['maxRange'] == None:
print(mod.item.name)
print(mod.itemModifiedAttributes.items())
raise ValueError('Projected module lacks a maxRange')
stats['optimal'] = mod.itemModifiedAttributes['maxRange']
stats['falloff'] = mod.itemModifiedAttributes['falloffEffectiveness'] or 0
attrDirectMap(['duration', 'capacitorNeed'], stats, mod)
projections.append(stats)
return projections
def getModuleNames(fit):
moduleNames = []
highSlotNames = []
midSlotNames = []
lowSlotNames = []
rigSlotNames = []
miscSlotNames = [] #subsystems ect
for mod in fit.modules:
if mod.slot == 3:
modSlotNames = highSlotNames
elif mod.slot == 2:
modSlotNames = midSlotNames
elif mod.slot == 1:
modSlotNames = lowSlotNames
elif mod.slot == 4:
modSlotNames = rigSlotNames
elif mod.slot == 5:
modSlotNames = miscSlotNames
try:
if mod.item != None:
if mod.charge != None:
modSlotNames.append(mod.item.name + ': ' + mod.charge.name)
else:
modSlotNames.append(mod.item.name)
else:
modSlotNames.append('Empty Slot')
except:
print(vars(mod))
print('could not find name for module')
print(fit.modules)
for modInfo in [['High Slots:'], highSlotNames, ['', 'Med Slots:'], midSlotNames, ['', 'Low Slots:'], lowSlotNames, ['', 'Rig Slots:'], rigSlotNames]:
moduleNames.extend(modInfo)
if len(miscSlotNames) > 0:
moduleNames.append('')
moduleNames.append('Subsystems:')
moduleNames.extend(miscSlotNames)
droneNames = []
fighterNames = []
for drone in fit.drones:
if drone.amountActive > 0:
droneNames.append("%s x%s" % (drone.item.name, drone.amount))
for fighter in fit.fighters:
if fighter.amountActive > 0:
fighterNames.append("%s x%s" % (fighter.item.name, fighter.amountActive))
if len(droneNames) > 0:
moduleNames.append('')
moduleNames.append('Drones:')
moduleNames.extend(droneNames)
if len(fighterNames) > 0:
moduleNames.append('')
moduleNames.append('Fighters:')
moduleNames.extend(fighterNames)
if len(fit.implants) > 0:
moduleNames.append('')
moduleNames.append('Implants:')
for implant in fit.implants:
moduleNames.append(implant.item.name)
if len(fit.commandFits) > 0:
moduleNames.append('')
moduleNames.append('Command Fits:')
for commandFit in fit.commandFits:
moduleNames.append(commandFit.name)
return moduleNames
def getWeaponSystemData(fit):
weaponSystems = []
groups = {}
for mod in fit.modules:
if mod.dps > 0:
keystr = str(mod.itemID) + '-' + str(mod.chargeID)
if keystr in groups:
groups[keystr][1] += 1
else:
groups[keystr] = [mod, 1]
for wepGroup in groups:
stats = groups[wepGroup][0]
c = groups[wepGroup][1]
tracking = 0
maxVelocity = 0
explosionDelay = 0
damageReductionFactor = 0
explosionRadius = 0
explosionVelocity = 0
aoeFieldRange = 0
if stats.hardpoint == 2:
tracking = stats.itemModifiedAttributes['trackingSpeed']
typeing = 'Turret'
name = stats.item.name + ', ' + stats.charge.name
elif stats.hardpoint == 1 or 'Bomb Launcher' in stats.item.name:
maxVelocity = stats.chargeModifiedAttributes['maxVelocity']
explosionDelay = stats.chargeModifiedAttributes['explosionDelay']
damageReductionFactor = stats.chargeModifiedAttributes['aoeDamageReductionFactor']
explosionRadius = stats.chargeModifiedAttributes['aoeCloudSize']
explosionVelocity = stats.chargeModifiedAttributes['aoeVelocity']
typeing = 'Missile'
name = stats.item.name + ', ' + stats.charge.name
elif stats.hardpoint == 0:
aoeFieldRange = stats.itemModifiedAttributes['empFieldRange']
typeing = 'SmartBomb'
name = stats.item.name
statDict = {'dps': stats.dps * c, 'capUse': stats.capUse * c, 'falloff': stats.falloff,\
'type': typeing, 'name': name, 'optimal': stats.maxRange,\
'numCharges': stats.numCharges, 'numShots': stats.numShots, 'reloadTime': stats.reloadTime,\
'cycleTime': stats.cycleTime, 'volley': stats.volley * c, 'tracking': tracking,\
'maxVelocity': maxVelocity, 'explosionDelay': explosionDelay, 'damageReductionFactor': damageReductionFactor,\
'explosionRadius': explosionRadius, 'explosionVelocity': explosionVelocity, 'aoeFieldRange': aoeFieldRange\
}
weaponSystems.append(statDict)
#if fit.droneDPS > 0:
for drone in fit.drones:
if drone.dps[0] > 0 and drone.amountActive > 0:
newTracking = drone.itemModifiedAttributes['trackingSpeed'] / (drone.itemModifiedAttributes['optimalSigRadius'] / 40000)
statDict = {'dps': drone.dps[0], 'cycleTime': drone.cycleTime, 'type': 'Drone',\
'optimal': drone.maxRange, 'name': drone.item.name, 'falloff': drone.falloff,\
'maxSpeed': drone.itemModifiedAttributes['maxVelocity'], 'tracking': newTracking,\
'volley': drone.dps[1]\
}
weaponSystems.append(statDict)
for fighter in fit.fighters:
if fighter.dps[0] > 0 and fighter.amountActive > 0:
abilities = []
#for ability in fighter.abilities:
if 'fighterAbilityAttackMissileDamageEM' in fighter.itemModifiedAttributes:
baseRef = 'fighterAbilityAttackMissile'
baseRefDam = baseRef + 'Damage'
damageReductionFactor = log(fighter.itemModifiedAttributes[baseRef + 'ReductionFactor']) / log(fighter.itemModifiedAttributes[baseRef + 'ReductionSensitivity'])
abBaseDamage = fighter.itemModifiedAttributes[baseRefDam + 'EM'] + fighter.itemModifiedAttributes[baseRefDam + 'Therm'] + fighter.itemModifiedAttributes[baseRefDam + 'Exp'] + fighter.itemModifiedAttributes[baseRefDam + 'Kin']
abDamage = abBaseDamage * fighter.itemModifiedAttributes[baseRefDam + 'Multiplier']
ability = {'name': 'RegularAttack', 'volley': abDamage * fighter.amountActive, 'explosionRadius': fighter.itemModifiedAttributes[baseRef + 'ExplosionRadius'],\
'explosionVelocity': fighter.itemModifiedAttributes[baseRef + 'ExplosionVelocity'], 'optimal': fighter.itemModifiedAttributes[baseRef + 'RangeOptimal'],\
'damageReductionFactor': damageReductionFactor, 'rof': fighter.itemModifiedAttributes[baseRef + 'Duration'],\
}
abilities.append(ability)
if 'fighterAbilityMissilesDamageEM' in fighter.itemModifiedAttributes:
baseRef = 'fighterAbilityMissiles'
baseRefDam = baseRef + 'Damage'
damageReductionFactor = log(fighter.itemModifiedAttributes[baseRefDam + 'ReductionFactor']) / log(fighter.itemModifiedAttributes[baseRefDam + 'ReductionSensitivity'])
abBaseDamage = fighter.itemModifiedAttributes[baseRefDam + 'EM'] + fighter.itemModifiedAttributes[baseRefDam + 'Therm'] + fighter.itemModifiedAttributes[baseRefDam + 'Exp'] + fighter.itemModifiedAttributes[baseRefDam + 'Kin']
abDamage = abBaseDamage * fighter.itemModifiedAttributes[baseRefDam + 'Multiplier']
ability = {'name': 'MissileAttack', 'volley': abDamage * fighter.amountActive, 'explosionRadius': fighter.itemModifiedAttributes[baseRef + 'ExplosionRadius'],\
'explosionVelocity': fighter.itemModifiedAttributes[baseRef + 'ExplosionVelocity'], 'optimal': fighter.itemModifiedAttributes[baseRef + 'Range'],\
'damageReductionFactor': damageReductionFactor, 'rof': fighter.itemModifiedAttributes[baseRef + 'Duration'],\
}
abilities.append(ability)
statDict = {'dps': fighter.dps[0], 'type': 'Fighter', 'name': fighter.item.name,\
'maxSpeed': fighter.itemModifiedAttributes['maxVelocity'], 'abilities': abilities, 'ehp': fighter.itemModifiedAttributes['shieldCapacity'] / 0.8875 * fighter.amountActive,\
'volley': fighter.dps[1], 'signatureRadius': fighter.itemModifiedAttributes['signatureRadius']\
}
weaponSystems.append(statDict)
return weaponSystems
def getWeaponBonusMultipliers(fit):
multipliers = {'turret': 1, 'launcher': 1, 'droneBandwidth': 1}
from eos.db import gamedata_session
from eos.gamedata import Traits
filterVal = Traits.typeID == fit.shipID
data = gamedata_session.query(Traits).options().filter(filterVal).all()
roleBonusMode = False
if len(data) == 0:
return multipliers
previousTypedBonus = 0
previousDroneTypeBonus = 0
for bonusText in data[0].traitText.splitlines():
bonusText = bonusText.lower()
if 'per skill level' in bonusText:
roleBonusMode = False
if 'role bonus' in bonusText or 'misc bonus' in bonusText:
roleBonusMode = True
multi = 1
if 'damage' in bonusText and not any(e in bonusText for e in ['control', 'heat']):
splitText = bonusText.split('%')
if (float(splitText[0]) > 0) == False:
print('damage bonus split did not parse correctly!')
print(float(splitText[0]))
if roleBonusMode:
addedMulti = float(splitText[0])
else:
addedMulti = float(splitText[0]) * 5
if any(e in bonusText for e in [' em', 'thermal', 'kinetic', 'explosive']):
if addedMulti > previousTypedBonus:
previousTypedBonus = addedMulti
else:
addedMulti = 0
if any(e in bonusText for e in ['heavy drone', 'medium drone', 'light drone', 'sentry drone']):
if addedMulti > previousDroneTypeBonus:
previousDroneTypeBonus = addedMulti
else:
addedMulti = 0
multi = 1 + (addedMulti / 100)
elif 'rate of fire' in bonusText:
splitText = bonusText.split('%')
if (float(splitText[0]) > 0) == False:
print('rate of fire bonus split did not parse correctly!')
print(float(splitText[0]))
if roleBonusMode:
rofMulti = float(splitText[0])
else:
rofMulti = float(splitText[0]) * 5
multi = 1 / (1 - (rofMulti / 100))
if multi > 1:
if 'drone' in bonusText.lower():
multipliers['droneBandwidth'] *= multi
elif 'turret' in bonusText.lower():
multipliers['turret'] *= multi
elif any(e in bonusText for e in ['missile', 'torpedo']):
multipliers['launcher'] *= multi
return multipliers
def getShipSize(groupID):
# Sizings are somewhat arbitrary but allow for a more managable number of top level groupings in a tree structure.
shipSizes = ['Frigate', 'Destroyer', 'Cruiser', 'Battlecruiser', 'Battleship', 'Capital', 'Industrial', 'Misc']
if groupID in [25, 31, 237, 324, 830, 831, 834, 893, 1283, 1527]:
return shipSizes[0]
elif groupID in [420, 541, 1305, 1534]:
return shipSizes[1]
elif groupID in [26, 358, 832, 833, 894, 906, 963]:
return shipSizes[2]
elif groupID in [419, 540, 1201]:
return shipSizes[3]
elif groupID in [27, 381, 898, 900]:
return shipSizes[4]
elif groupID in [30, 485, 513, 547, 659, 883, 902, 1538]:
return shipSizes[5]
elif groupID in [28, 380, 1202, 463, 543, 941]:
return shipSizes[6]
elif groupID in [29, 1022]:
return shipSizes[7]
else:
sizeNotFoundMsg = 'ShipSize not found for groupID: ' + str(groupID)
print(sizeNotFoundMsg)
return sizeNotFoundMsg
def parseNeededFitDetails(fit, groupID):
includeShipTypeData = groupID > 0
fitID = fit.ID
if len(fit.modules) > 0:
fitName = fit.ship.name + ': ' + fit.name
else:
fitName = fit.name
print('')
print('name: ' + fit.name)
fitL = Fit()
fitL.recalc(fit)
fit = eos.db.getFit(fitID)
fitModAttr = fit.ship.itemModifiedAttributes
propData = getPropData(fit, fitL)
print(fitModAttr['rigSize'])
print(propData)
mwdPropSpeed = fit.maxSpeed
if includeShipTypeData:
mwdPropSpeed = getT2MwdSpeed(fit, fitL)
projections = getOutgoingProjectionData(fit)
moduleNames = getModuleNames(fit)
weaponSystems = getWeaponSystemData(fit)
turretSlots = fitModAttr['turretSlotsLeft'] if fitModAttr['turretSlotsLeft'] is not None else 0
launcherSlots = fitModAttr['launcherSlotsLeft'] if fitModAttr['launcherSlotsLeft'] is not None else 0
droneBandwidth = fitModAttr['droneBandwidth'] if fitModAttr['droneBandwidth'] is not None else 0
weaponBonusMultipliers = getWeaponBonusMultipliers(fit)
effectiveTurretSlots = round(turretSlots * weaponBonusMultipliers['turret'], 2);
effectiveLauncherSlots = round(launcherSlots * weaponBonusMultipliers['launcher'], 2);
effectiveDroneBandwidth = round(droneBandwidth * weaponBonusMultipliers['droneBandwidth'], 2);
# Assume a T2 siege module for dreads
if groupID == 485:
effectiveTurretSlots *= 9.4
effectiveLauncherSlots *= 15
hullResonance = {
'exp': fitModAttr['explosiveDamageResonance'], 'kin': fitModAttr['kineticDamageResonance'], \
'therm': fitModAttr['thermalDamageResonance'], 'em': fitModAttr['emDamageResonance']
}
armorResonance = {
'exp': fitModAttr['armorExplosiveDamageResonance'], 'kin': fitModAttr['armorKineticDamageResonance'], \
'therm': fitModAttr['armorThermalDamageResonance'], 'em': fitModAttr['armorEmDamageResonance']
}
shieldResonance = {
'exp': fitModAttr['shieldExplosiveDamageResonance'], 'kin': fitModAttr['shieldKineticDamageResonance'], \
'therm': fitModAttr['shieldThermalDamageResonance'], 'em': fitModAttr['shieldEmDamageResonance']
}
resonance = {'hull': hullResonance, 'armor': armorResonance, 'shield': shieldResonance}
shipSize = getShipSize(groupID)
try:
parsable = {
'name': fitName, 'ehp': fit.ehp, 'droneDPS': fit.droneDPS, \
'droneVolley': fit.droneVolley, 'hp': fit.hp, 'maxTargets': fit.maxTargets, \
'maxSpeed': fit.maxSpeed, 'weaponVolley': fit.weaponVolley, 'totalVolley': fit.totalVolley,\
'maxTargetRange': fit.maxTargetRange, 'scanStrength': fit.scanStrength,\
'weaponDPS': fit.weaponDPS, 'alignTime': fit.alignTime, 'signatureRadius': fitModAttr['signatureRadius'],\
'weapons': weaponSystems, 'scanRes': fitModAttr['scanResolution'],\
'projectedModules': fit.projectedModules, 'capUsed': fit.capUsed, 'capRecharge': fit.capRecharge,\
'rigSlots': fitModAttr['rigSlots'], 'lowSlots': fitModAttr['lowSlots'],\
'midSlots': fitModAttr['medSlots'], 'highSlots': fitModAttr['hiSlots'],\
'turretSlots': fitModAttr['turretSlotsLeft'], 'launcherSlots': fitModAttr['launcherSlotsLeft'],\
'powerOutput': fitModAttr['powerOutput'], 'rigSize': fitModAttr['rigSize'],\
'effectiveTurrets': effectiveTurretSlots, 'effectiveLaunchers': effectiveLauncherSlots,\
'effectiveDroneBandwidth': effectiveDroneBandwidth,\
'resonance': resonance, 'typeID': fit.shipID, 'groupID': groupID, 'shipSize': shipSize,\
'droneControlRange': fitModAttr['droneControlRange'], 'mass': fitModAttr['mass'],\
'moduleNames': moduleNames, 'projections': projections,\
'unpropedSpeed': propData['unpropedSpeed'], 'unpropedSig': propData['unpropedSig'],\
'usingMWD': propData['usingMWD'], 'mwdPropSpeed': mwdPropSpeed
}
except TypeError:
print('Error parsing fit:' + str(fit))
print(TypeError)
parsable = {'name': fitName + 'Fit could not be correctly parsed'}
stringified = json.dumps(parsable, skipkeys=True)
return stringified