Files
pyfa/service/ammo.py
2025-10-30 12:51:51 +01:00

202 lines
7.4 KiB
Python

# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import math
from collections import OrderedDict
import wx
from eos.const import FittingHardpoint
from eos.saveddata.module import Module
from eos.utils.stats import DmgTypes
from service.market import Market
_t = wx.GetTranslation
class Ammo:
instance = None
@classmethod
def getInstance(cls):
if cls.instance is None:
cls.instance = Ammo()
return cls.instance
@staticmethod
def getModuleFlatAmmo(mod):
sMkt = Market.getInstance()
if mod is None or mod.isEmpty:
return set()
chargeSet = set()
# Do not try to grab it for t3d modes which can also be passed as part of selection
if isinstance(mod, Module):
for charge in mod.getValidCharges():
if sMkt.getPublicityByItem(charge):
chargeSet.add(charge)
return chargeSet
@classmethod
def getModuleStructuredAmmo(cls, mod, ammo=None):
chargesFlat = cls.getModuleFlatAmmo(mod) if ammo is None else ammo
# Make sure we do not consider mining turrets as combat turrets
if mod.hardpoint == FittingHardpoint.TURRET and not mod.getModifiedItemAttr('miningAmount'):
def turretSorter(charge):
damage = 0
range_ = (mod.item.getAttribute('maxRange')) * \
(charge.getAttribute('weaponRangeMultiplier') or 1)
falloff = (mod.item.getAttribute('falloff') or 0) * \
(charge.getAttribute('fallofMultiplier') or 1)
for type_ in DmgTypes.names():
d = charge.getAttribute('%sDamage' % type_, default=0)
if d > 0:
damage += d
# Take optimal and falloff as range factor
rangeFactor = range_ + falloff
return -rangeFactor, charge.typeName.rsplit()[-2:], damage, charge.name
all = OrderedDict()
sub = []
prevNameBase = None
prevRange = None
for charge in sorted(chargesFlat, key=turretSorter):
if 'civilian' in charge.typeName.lower():
continue
currNameBase = ' '.join(charge.typeName.rsplit()[-2:])
currRange = charge.getAttribute('weaponRangeMultiplier')
if sub and (currRange != prevRange or currNameBase != prevNameBase):
all[sub[0].name] = sub
sub = []
sub.append(charge)
prevNameBase = currNameBase
prevRange = currRange
else:
if sub:
all[sub[0].name] = sub
return 'ddTurret', all
elif mod.hardpoint == FittingHardpoint.MISSILE and mod.item.name != 'Festival Launcher':
def getChargeDamageInfo(charge):
# Set up data storage for missile damage stuff
damageMap = {}
totalDamage = 0
# Fill them with the data about charge
for damageType in DmgTypes.names():
currentDamage = charge.getAttribute('{}Damage'.format(damageType)) or 0
damageMap[damageType] = currentDamage
totalDamage += currentDamage
# Detect type of ammo
chargeDamageType = None
for damageType in damageMap:
# If all damage belongs to certain type purely, set appropriate
# ammoType
if damageMap[damageType] == totalDamage:
chargeDamageType = damageType
break
# Else consider ammo as mixed damage
if chargeDamageType is None:
chargeDamageType = 'mixed'
return chargeDamageType, totalDamage
def missileSorter(charge):
# Get charge damage type and total damage
chargeDamageType, totalDamage = getChargeDamageInfo(charge)
# Find its position in sort list
try:
position = DmgTypes.names().index(chargeDamageType)
# Put charges which have non-standard damage type after charges with
# standard damage type
except ValueError:
position = math.inf
return position, totalDamage, charge.name
all = OrderedDict()
sub = []
prevType = None
for charge in sorted(chargesFlat, key=missileSorter):
currType = getChargeDamageInfo(charge)[0]
if sub and currType != prevType:
all[prevType] = sub
sub = []
sub.append(charge)
prevType = currType
else:
if sub:
all[prevType] = sub
return 'ddMissile', all
elif mod.item.group.name == 'Frequency Mining Laser':
def crystalSorter(charge):
if charge.name.endswith(' II'):
techLvl = 2
elif charge.name.endswith(' I'):
techLvl = 1
else:
techLvl = 0
if ' A ' in charge.name:
type_ = 'A'
elif ' B ' in charge.name:
type_ = 'B'
elif ' C ' in charge.name:
type_ = 'C'
else:
type_ = '0'
return type_, techLvl, charge.name
typeMap = {
253: 'a1',
254: 'a2',
255: 'a3',
256: 'a4',
257: 'a5',
258: 'a6',
259: 'r4',
260: 'r8',
261: 'r16',
262: 'r32',
263: 'r64',
859: 'err'}
prelim = {}
for charge in chargesFlat:
oreTypeList = charge.getAttribute('specializationAsteroidTypeList')
category = typeMap.get(oreTypeList, _t('Misc'))
prelim.setdefault(category, set()).add(charge)
final = OrderedDict()
for category, charges in prelim.items():
final[category] = sorted(charges, key=crystalSorter)
return 'miner', final
else:
def nameSorter(charge):
parts = charge.name.split(" ")
return [int(p) if p.isdigit() else p for p in parts]
return 'general', {'general': sorted(chargesFlat, key=nameSorter)}