Merge branch 'master' into efs_port

This commit is contained in:
MaruMaruOO
2018-09-27 02:52:49 -04:00
55 changed files with 372 additions and 253 deletions

View File

@@ -24,10 +24,10 @@ saveInRoot = False
# Version data
version = "2.3.1"
version = "2.4.0"
tag = "Stable"
expansionName = "YC120.7"
expansionVersion = "1.2"
expansionName = "YC120.8"
expansionVersion = "1.0"
evemonMinVersion = "4081"
minItemSearchLength = 3

View File

@@ -1,7 +1,7 @@
# boosterShieldCapacityPenalty
#
# Used by:
# Implants from group: Booster (12 of 66)
# Implants from group: Booster (12 of 65)
type = "boosterSideEffect"
# User-friendly name for the side effect

View File

@@ -3,9 +3,9 @@
# Used by:
# Ships from group: Black Ops (5 of 5)
# Ships from group: Blockade Runner (4 of 4)
# Ships from group: Covert Ops (7 of 7)
# Ships from group: Covert Ops (8 of 8)
# Ships from group: Expedition Frigate (2 of 2)
# Ships from group: Force Recon Ship (8 of 8)
# Ships from group: Force Recon Ship (9 of 9)
# Ships from group: Stealth Bomber (5 of 5)
# Ships named like: Stratios (2 of 2)
# Subsystems named like: Defensive Covert Reconfiguration (4 of 4)

View File

@@ -1,7 +1,7 @@
# covertOpsCloakCpuPercentBonus1
#
# Used by:
# Ships from group: Covert Ops (5 of 7)
# Ships from group: Covert Ops (6 of 8)
type = "passive"
runTime = "early"

View File

@@ -0,0 +1,9 @@
# covertOpsWarpResistance
#
# Used by:
# Ships from group: Covert Ops (5 of 8)
type = "passive"
def handler(fit, src, context):
fit.ship.increaseItemAttr("warpFactor", src.getModifiedItemAttr("eliteBonusCovertOps1"), skill="Covert Ops")

View File

@@ -1,7 +1,7 @@
# cynosuralDurationBonus
#
# Used by:
# Ships from group: Force Recon Ship (7 of 8)
# Ships from group: Force Recon Ship (8 of 9)
type = "passive"

View File

@@ -1,7 +1,7 @@
# cynosuralTheoryConsumptionBonus
#
# Used by:
# Ships from group: Force Recon Ship (7 of 8)
# Ships from group: Force Recon Ship (8 of 9)
# Skill: Cynosural Field Theory
type = "passive"

View File

@@ -1,7 +1,7 @@
# eliteBonusCoverOpsScanProbeStrength2
#
# Used by:
# Ships from group: Covert Ops (7 of 7)
# Ships from group: Covert Ops (8 of 8)
type = "passive"

View File

@@ -0,0 +1,10 @@
# eliteBonusMaxDmgMultiBonusAdd
#
# Used by:
# Ship: Hydra
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), "damageMultiplierBonusMax",
src.getModifiedItemAttr("eliteBonusCovertOps3"), skill="Covert Ops")

View File

@@ -0,0 +1,10 @@
# eliteBonusReconMaxDmgMultiMaxHPT
#
# Used by:
# Ship: Tiamat
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), "damageMultiplierBonusMax",
src.getModifiedItemAttr("eliteBonusReconShip3"), skill="Recon Ships")

View File

@@ -0,0 +1,10 @@
# eliteBonusReconScanProbeStrength2
#
# Used by:
# Ship: Tiamat
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"), "baseSensorStrength",
src.getModifiedItemAttr("eliteBonusReconShip2"), skill="Recon Ships")

View File

@@ -7,3 +7,9 @@ type = "active"
def handler(fit, module, context):
fit.ship.forceItemAttr("disallowAssistance", module.getModifiedItemAttr("disallowAssistance"))
for scanType in ("Gravimetric", "Magnetometric", "Radar", "Ladar"):
fit.ship.boostItemAttr(
"scan{}Strength".format(scanType),
module.getModifiedItemAttr("scan{}StrengthPercent".format(scanType)),
stackingPenalties=True
)

View File

@@ -1,7 +1,7 @@
# minigameVirusStrengthBonus
#
# Used by:
# Ships from group: Covert Ops (7 of 7)
# Ships from group: Covert Ops (7 of 8)
# Ships named like: Stratios (2 of 2)
# Subsystems named like: Defensive Covert Reconfiguration (4 of 4)
# Ship: Astero

View File

@@ -1,7 +1,7 @@
# reconShipCloakCpuBonus1
#
# Used by:
# Ships from group: Force Recon Ship (6 of 8)
# Ships from group: Force Recon Ship (7 of 9)
type = "passive"
runTime = "early"

View File

@@ -2,7 +2,9 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
# Ship: Leshak
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -1,6 +1,7 @@
# shipbonusPCTDamagePC1
#
# Used by:
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
type = "passive"

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
type = "passive"

View File

@@ -1,6 +1,7 @@
# shipbonusPCTTrackingPC2
#
# Used by:
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -2,7 +2,9 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
# Ship: Leshak
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -2,7 +2,9 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
# Ship: Leshak
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -2,7 +2,9 @@
#
# Used by:
# Ship: Damavik
# Ship: Hydra
# Ship: Leshak
# Ship: Tiamat
# Ship: Vedmak
type = "passive"

View File

@@ -1,7 +1,7 @@
# shipBonusSurveyProbeExplosionDelaySkillSurveyCovertOps3
#
# Used by:
# Ships from group: Covert Ops (5 of 7)
# Ships from group: Covert Ops (5 of 8)
type = "passive"

View File

@@ -685,15 +685,13 @@ class Fit(object):
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"armorDamageAmount", value, stackingPenalties=True)
if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or
mod.item.requiresSkill("Shield Emission Systems"),
"capacitorNeed", value, stackingPenalties=True)
if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster_shield_bonus
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"),
"shieldBonus", value, stackingPenalties=True)
if warfareBuffID == 89: # AOE_Beacon_filament_cloud_ancillary_charge_usage
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or
mod.item.requiresSkill("Shield Emission Systems"),
"chargeRate", value, stackingPenalties=True)
if warfareBuffID == 89: # AOE_Beacon_filament_cloud_shield_booster_duration
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"),
"duration", value, stackingPenalties=True)
# Abysmal Weather Effects

BIN
eve.db

Binary file not shown.

View File

@@ -195,7 +195,7 @@ class ModuleAmmoPicker(ContextMenu):
type_ = currType
item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize())
bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui")
bitmap = BitmapLoader.getBitmap("%s_small" % type_, "gui")
if bitmap is not None:
item.SetBitmap(bitmap)

View File

@@ -125,11 +125,11 @@ class ItemCompare(wx.Panel):
# Remember to reduce by 1, because the attrs array
# starts at 0 while the list has the item name as column 0.
attr = str(list(self.attrs.keys())[sort - 1])
func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None
func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else 0.0
except IndexError:
# Clicked on a column that's not part of our array (price most likely)
self.sortReverse = False
func = lambda _val: _val.attributes['metaLevel'].value if 'metaLevel' in _val.attributes else None
func = lambda _val: _val.attributes['metaLevel'].value if 'metaLevel' in _val.attributes else 0.0
self.items = sorted(self.items, key=func, reverse=self.sortReverse)

View File

@@ -4,8 +4,10 @@ import wx
from service.fit import Fit
from gui.bitmap_loader import BitmapLoader
import gui.globalEvents as GE
from gui.preferenceView import PreferenceView
from service.settings import EOSSettings
import gui.mainFrame
logger = logging.getLogger(__name__)
@@ -21,6 +23,7 @@ class PFFittingEnginePref(PreferenceView):
# noinspection PyAttributeOutsideInit
def populatePanel(self, panel):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
mainSizer = wx.BoxSizer(wx.VERTICAL)
@@ -98,6 +101,9 @@ class PFFittingEnginePref(PreferenceView):
def OnCBGlobalForceReloadStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalForceReload"] = self.cbGlobalForceReload.GetValue()
fitID = self.mainFrame.getActiveFit()
self.sFit.refreshFit(fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def OnCBStrictSkillLevelsChange(self, event):
self.engine_settings.set("strictSkillLevels", self.cbStrictSkillLevels.GetValue())

View File

@@ -1,6 +1,7 @@
# noinspection PyPackageRequirements
import wx
import gui.utils.draw as drawUtils
import gui.mainFrame
SB_ITEM_NORMAL = 0
SB_ITEM_SELECTED = 1
@@ -245,6 +246,7 @@ class SFBrowserItem(wx.Window):
self.highlighted = False
self.selected = False
self.bkBitmap = None
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.canBeDragged = False
@@ -311,6 +313,10 @@ class SFBrowserItem(wx.Window):
self.canBeDragged = mode
def OnLeftUp(self, event):
if self.mainFrame.supress_left_up:
wx.Yield()
self.mainFrame.supress_left_up = False
return
if self.HasCapture():
self.ReleaseMouse()

View File

@@ -45,21 +45,21 @@ class Price(ViewColumn):
if stuff.isEmpty:
return ""
price = stuff.item.price.price
price = stuff.item.price
if not price:
return ""
if not price or not price.isValid:
return False
if isinstance(stuff, Drone) or isinstance(stuff, Cargo):
price *= stuff.amount
price.price *= stuff.amount
return formatAmount(price, 3, 3, 9, currency=True)
return formatAmount(price.price, 3, 3, 9, currency=True)
def delayedText(self, mod, display, colItem):
sPrice = ServicePrice.getInstance()
def callback(item):
price = item.item.price
price = item[0]
text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else ""
if price.failed:
text += " (!)"

View File

@@ -477,7 +477,7 @@ class FittingView(d.Display):
return
if getattr(mod2, "modPosition") is not None:
if clone and mod2.isEmpty:
if clone and mod2.isEmpty and mod1.getModifiedItemAttr("maxGroupFitted", 0) < 1.0:
sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
else:
sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)

View File

@@ -552,24 +552,6 @@ class _TabRenderer:
bmp,
self.left_width + self.padding - bmp.GetWidth() / 2,
(height - bmp.GetHeight()) / 2)
text_start = self.left_width + self.padding + bmp.GetWidth() / 2
else:
text_start = self.left_width
mdc.SetFont(self.font)
maxsize = self.tab_width \
- text_start \
- self.right_width \
- self.padding * 4
color = self.selected_color if self.selected else self.inactive_color
mdc.SetTextForeground(color_utils.GetSuitable(color, 1))
# draw text (with no ellipses)
text = draw.GetPartialText(mdc, self.text, maxsize, "")
tx, ty = mdc.GetTextExtent(text)
mdc.DrawText(text, text_start + self.padding, height / 2 - ty / 2)
# draw close button
if self.closeable:
@@ -596,6 +578,30 @@ class _TabRenderer:
bmp = wx.Bitmap(img)
self.tab_bitmap = bmp
# We draw the text separately in order to draw it directly on the native DC, rather than a memory one, because
# drawing text on a memory DC draws it blurry on HD/Retina screens
def DrawText(self, dc):
height = self.tab_height
dc.SetFont(self.font)
if self.tab_img:
text_start = self.left_width + self.padding + self.tab_img.GetWidth() / 2
else:
text_start = self.left_width
maxsize = self.tab_width \
- text_start \
- self.right_width \
- self.padding * 4
color = self.selected_color if self.selected else self.inactive_color
dc.SetTextForeground(color_utils.GetSuitable(color, 1))
# draw text (with no ellipses)
text = draw.GetPartialText(dc, self.text, maxsize, "")
tx, ty = dc.GetTextExtent(text)
dc.DrawText(text, text_start + self.padding, height / 2 - ty / 2)
def __repr__(self):
return "_TabRenderer(text={}, disabled={}) at {}".format(
self.text, self.disabled, hex(id(self))
@@ -1145,6 +1151,10 @@ class _TabsContainer(wx.Panel):
img = img.AdjustChannels(1, 1, 1, 0.85)
bmp = wx.Bitmap(img)
mdc.DrawBitmap(bmp, posx, posy, True)
mdc.SetDeviceOrigin(posx, posy)
tab.DrawText(mdc)
mdc.SetDeviceOrigin(0, 0)
else:
selected = tab
@@ -1164,6 +1174,10 @@ class _TabsContainer(wx.Panel):
mdc.DrawBitmap(bmp, posx, posy, True)
mdc.SetDeviceOrigin(posx, posy)
selected.DrawText(mdc)
mdc.SetDeviceOrigin(0, 0)
def OnErase(self, event):
pass

View File

@@ -41,7 +41,7 @@ class CopySelectDialog(wx.Dialog):
CopySelectDialog.copyFormatEftImps: "EFT text format",
CopySelectDialog.copyFormatXml: "EVE native XML format",
CopySelectDialog.copyFormatDna: "A one-line text format",
CopySelectDialog.copyFormatEsi: "A JSON format used for EVE CREST",
CopySelectDialog.copyFormatEsi: "A JSON format used for ESI",
CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format",
CopySelectDialog.copyFormatEfs: "JSON data format used by EFS"}
selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats,

View File

@@ -26,6 +26,7 @@ import traceback
import config
from logbook import Logger
from service.prereqsCheck import version_block
import datetime
pyfalog = Logger(__name__)
@@ -63,6 +64,11 @@ class ErrorFrame(wx.Frame):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600),
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP)
from eos.config import gamedata_version, gamedata_date
time = datetime.datetime.fromtimestamp(int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S')
version = "pyfa v" + config.getVersion() + '\nEVE Data Version: {} ({})\n\n'.format(gamedata_version, time) # gui.aboutData.versionString
desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \
"information about how this was triggered. Please contact the developers with the\n" \
"information provided through the EVE Online forums or file a GitHub issue."
@@ -97,7 +103,7 @@ class ErrorFrame(wx.Frame):
# mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5)
self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition,
self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version + version_block.strip(), wx.DefaultPosition,
(-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP)
self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5)

View File

@@ -155,6 +155,7 @@ class MainFrame(wx.Frame):
pyfalog.debug("Initialize MainFrame")
self.title = title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
self.supress_left_up = False
MainFrame.__instance = self
@@ -433,6 +434,7 @@ class MainFrame(wx.Frame):
style=wx.FD_SAVE,
defaultFile=defaultFile)
if dlg.ShowModal() == wx.ID_OK:
self.supress_left_up = True
format_ = dlg.GetFilterIndex()
path = dlg.GetPath()
if format_ == 0:
@@ -977,6 +979,7 @@ class MainFrame(wx.Frame):
)
if dlg.ShowModal() == wx.ID_OK:
self.supress_left_up = True
self.waitDialog = wx.BusyInfo("Importing Character...")
sCharacter = Character.getInstance()
sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback)

BIN
imgs/icons/10160.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

BIN
imgs/icons/10851.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

BIN
imgs/icons/21785.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

BIN
imgs/icons/22029.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

BIN
imgs/icons/22030.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

BIN
imgs/icons/22031.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

BIN
imgs/icons/22034.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

BIN
imgs/icons/22036.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

BIN
imgs/icons/22041.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

BIN
imgs/renders/21344.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/renders/21362.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/renders/21405.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
imgs/renders/21406.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -141,6 +141,7 @@ def get_icon_file(res_path, size):
icon for it. Return as PIL image object down-
scaled for use in pyfa.
"""
res_path = res_path.replace('//', '/') #1703
if res_path not in res_index:
return None
res_icon = res_index[res_path]
@@ -192,7 +193,10 @@ if toadd:
print(('Adding {} icons...'.format(len(toadd))))
missing = set()
for fname in sorted(toadd):
icon = icon_json[str(fname)]
icon = icon_json.get(str(fname), None)
if icon is None:
print("Can't find iconID {}".format(fname))
continue
key = icon['iconFile'].lower()
icon = get_icon_file(key, ICON_SIZE)
if icon is None:

View File

@@ -25,7 +25,7 @@ import re
# Add eos root path to sys.path so we can import ourselves
path = os.path.dirname(__file__)
sys.path.insert(0, os.path.realpath(os.path.join(path, "..")))
sys.path.insert(0, os.path.realpath(os.path.join(path, '..')))
import json
import argparse
@@ -54,65 +54,65 @@ def main(db, json_path):
# Config dict
tables = {
"clonegrades": eos.gamedata.AlphaCloneSkill,
"dgmattribs": eos.gamedata.AttributeInfo,
"dgmeffects": eos.gamedata.Effect,
"dgmtypeattribs": eos.gamedata.Attribute,
"dgmtypeeffects": eos.gamedata.ItemEffect,
"dgmunits": eos.gamedata.Unit,
"evecategories": eos.gamedata.Category,
"evegroups": eos.gamedata.Group,
"invmetagroups": eos.gamedata.MetaGroup,
"invmetatypes": eos.gamedata.MetaType,
"evetypes": eos.gamedata.Item,
"phbtraits": eos.gamedata.Traits,
"phbmetadata": eos.gamedata.MetaData,
"mapbulk_marketGroups": eos.gamedata.MarketGroup,
'clonegrades': eos.gamedata.AlphaCloneSkill,
'dgmattribs': eos.gamedata.AttributeInfo,
'dgmeffects': eos.gamedata.Effect,
'dgmtypeattribs': eos.gamedata.Attribute,
'dgmtypeeffects': eos.gamedata.ItemEffect,
'dgmunits': eos.gamedata.Unit,
'evecategories': eos.gamedata.Category,
'evegroups': eos.gamedata.Group,
'invmetagroups': eos.gamedata.MetaGroup,
'invmetatypes': eos.gamedata.MetaType,
'evetypes': eos.gamedata.Item,
'phbtraits': eos.gamedata.Traits,
'phbmetadata': eos.gamedata.MetaData,
'mapbulk_marketGroups': eos.gamedata.MarketGroup,
}
fieldMapping = {
"dgmattribs": {
"displayName_en-us": "displayName"
'dgmattribs': {
'displayName': 'displayName'
},
"dgmeffects": {
"displayName_en-us": "displayName",
"description_en-us": "description"
'dgmeffects': {
'displayName': 'displayName',
'description': 'description'
},
"dgmunits": {
"displayName_en-us": "displayName"
'dgmunits': {
'displayName': 'displayName'
},
#icons???
"evecategories": {
"categoryName_en-us": "categoryName"
'evecategories': {
'categoryName': 'categoryName'
},
"evegroups": {
"groupName_en-us": "groupName"
'evegroups': {
'groupName': 'groupName'
},
"invmetagroups": {
"metaGroupName_en-us": "metaGroupName"
'invmetagroups': {
'metaGroupName': 'metaGroupName'
},
"evetypes": {
"typeName_en-us": "typeName",
"description_en-us": "description"
'evetypes': {
'typeName': 'typeName',
'description': 'description'
},
#phbtraits???
"mapbulk_marketGroups": {
"marketGroupName_en-us": "marketGroupName",
"description_en-us": "description"
'mapbulk_marketGroups': {
'marketGroupName': 'marketGroupName',
'description': 'description'
}
}
rowsInValues = (
"evetypes",
"evegroups",
"evecategories"
'evetypes',
'evegroups',
'evecategories'
)
def convertIcons(data):
new = []
for k, v in list(data.items()):
v["iconID"] = k
v['iconID'] = k
new.append(v)
return new
@@ -126,23 +126,23 @@ def main(db, json_path):
check = {}
for ID in data:
for skill in data[ID]["skills"]:
for skill in data[ID]['skills']:
newData.append({
"alphaCloneID": int(ID),
"alphaCloneName": "Alpha Clone",
"typeID": skill["typeID"],
"level": skill["level"]})
'alphaCloneID': int(ID),
'alphaCloneName': 'Alpha Clone',
'typeID': skill['typeID'],
'level': skill['level']})
if ID not in check:
check[ID] = {}
check[ID][int(skill["typeID"])] = int(skill["level"])
check[ID][int(skill['typeID'])] = int(skill['level'])
if not functools.reduce(lambda a, b: a if a == b else False, [v for _, v in check.items()]):
raise Exception("Alpha Clones not all equal")
raise Exception('Alpha Clones not all equal')
newData = [x for x in newData if x['alphaCloneID'] == 1]
if len(newData) == 0:
raise Exception("Alpha Clone processing failed")
raise Exception('Alpha Clone processing failed')
return newData
@@ -150,28 +150,28 @@ def main(db, json_path):
def convertSection(sectionData):
sectionLines = []
headerText = "<b>{}</b>".format(sectionData["header"])
headerText = '<b>{}</b>'.format(sectionData['header'])
sectionLines.append(headerText)
for bonusData in sectionData["bonuses"]:
prefix = "{} ".format(bonusData["number"]) if "number" in bonusData else ""
bonusText = "{}{}".format(prefix, bonusData["text"].replace("\u00B7", "\u2022 "))
for bonusData in sectionData['bonuses']:
prefix = '{} '.format(bonusData['number']) if 'number' in bonusData else ''
bonusText = '{}{}'.format(prefix, bonusData['text'].replace('\u00B7', '\u2022 '))
sectionLines.append(bonusText)
sectionLine = "<br />\n".join(sectionLines)
sectionLine = '<br />\n'.join(sectionLines)
return sectionLine
newData = []
for row in data:
typeLines = []
typeId = row["typeID"]
traitData = row["traits"]
for skillData in sorted(traitData.get("skills", ()), key=lambda i: i["header"]):
typeId = row['typeID']
traitData = row['traits']
for skillData in sorted(traitData.get('skills', ()), key=lambda i: i['header']):
typeLines.append(convertSection(skillData))
if "role" in traitData:
typeLines.append(convertSection(traitData["role"]))
if "misc" in traitData:
typeLines.append(convertSection(traitData["misc"]))
traitLine = "<br />\n<br />\n".join(typeLines)
newRow = {"typeID": typeId, "traitText": traitLine}
if 'role' in traitData:
typeLines.append(convertSection(traitData['role']))
if 'misc' in traitData:
typeLines.append(convertSection(traitData['misc']))
traitLine = '<br />\n<br />\n'.join(typeLines)
newRow = {'typeID': typeId, 'traitText': traitLine}
newData.append(newRow)
return newData
@@ -179,15 +179,15 @@ def main(db, json_path):
# Dump all data to memory so we can easely cross check ignored rows
for jsonName, cls in tables.items():
with open(os.path.join(jsonPath, "{}.json".format(jsonName)), encoding="utf-8") as f:
with open(os.path.join(jsonPath, '{}.json'.format(jsonName)), encoding='utf-8') as f:
tableData = json.load(f)
if jsonName in rowsInValues:
tableData = list(tableData.values())
if jsonName == "icons":
if jsonName == 'icons':
tableData = convertIcons(tableData)
if jsonName == "phbtraits":
if jsonName == 'phbtraits':
tableData = convertTraits(tableData)
if jsonName == "clonegrades":
if jsonName == 'clonegrades':
tableData = convertClones(tableData)
data[jsonName] = tableData
@@ -195,8 +195,8 @@ def main(db, json_path):
# Sometimes CCP unpublishes some items we want to have published, we
# can do it here - just add them to initial set
eveTypes = set()
for row in data["evetypes"]:
if (row["published"]
for row in data['evetypes']:
if (row['published']
or row['groupID'] == 1306 # group Ship Modifiers, for items like tactical t3 ship modes
or row['typeName'].startswith('Civilian') # Civilian weapons
or row['typeID'] in (41549, 41548, 41551, 41550) # Micro Bombs (Fighters)
@@ -204,14 +204,14 @@ def main(db, json_path):
1882,
1975,
1971,
1983 # the "container" for the abysmal environments
) # Abysmal weather (environment)
1983 # the "container" for the abyssal environments
) # Abyssal weather (environment)
):
eveTypes.add(row["typeID"])
eveTypes.add(row['typeID'])
# ignore checker
def isIgnored(file, row):
if file in ("evetypes", "dgmtypeeffects", "dgmtypeattribs", "invmetatypes") and row['typeID'] not in eveTypes:
if file in ('evetypes', 'dgmtypeeffects', 'dgmtypeattribs', 'invmetatypes') and row['typeID'] not in eveTypes:
return True
return False
@@ -220,31 +220,31 @@ def main(db, json_path):
fieldMap = fieldMapping.get(jsonName, {})
tmp = []
print("processing {}".format(jsonName))
print('processing {}'.format(jsonName))
for row in table:
# We don't care about some kind of rows, filter it out if so
if not isIgnored(jsonName, row):
if jsonName == 'evetypes' and row["typeName"].startswith('Civilian'): # Apparently people really want Civilian modules available
row["published"] = True
if jsonName == 'evetypes' and row['typeName'].startswith('Civilian'): # Apparently people really want Civilian modules available
row['published'] = True
instance = tables[jsonName]()
# fix for issue 80
if jsonName is "icons" and "res:/ui/texture/icons/" in str(row["iconFile"]).lower():
row["iconFile"] = row["iconFile"].lower().replace("res:/ui/texture/icons/", "").replace(".png", "")
if jsonName is 'icons' and 'res:/ui/texture/icons/' in str(row['iconFile']).lower():
row['iconFile'] = row['iconFile'].lower().replace('res:/ui/texture/icons/', '').replace('.png', '')
# with res:/ui... references, it points to the actual icon file (including it's size variation of #_size_#)
# strip this info out and get the identifying info
split = row['iconFile'].split('_')
if len(split) == 3:
row['iconFile'] = "{}_{}".format(split[0], split[2])
if jsonName is "icons" and "modules/" in str(row["iconFile"]).lower():
row["iconFile"] = row["iconFile"].lower().replace("modules/", "").replace(".png", "")
row['iconFile'] = '{}_{}'.format(split[0], split[2])
if jsonName is 'icons' and 'modules/' in str(row['iconFile']).lower():
row['iconFile'] = row['iconFile'].lower().replace('modules/', '').replace('.png', '')
if jsonName is "clonegrades":
if (row["alphaCloneID"] not in tmp):
if jsonName is 'clonegrades':
if (row['alphaCloneID'] not in tmp):
cloneParent = eos.gamedata.AlphaClone()
setattr(cloneParent, "alphaCloneID", row["alphaCloneID"])
setattr(cloneParent, "alphaCloneName", row["alphaCloneName"])
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
eos.db.gamedata_session.add(cloneParent)
tmp.append(row['alphaCloneID'])
@@ -256,7 +256,7 @@ def main(db, json_path):
eos.db.gamedata_session.add(instance)
# quick and dirty hack to get this data in
with open(os.path.join(jsonPath, "dynamicAttributes.json"), encoding="utf-8") as f:
with open(os.path.join(jsonPath, 'dynamicAttributes.json'), encoding='utf-8') as f:
bulkdata = json.load(f)
for mutaID, data in bulkdata.items():
muta = eos.gamedata.DynamicItem()
@@ -283,25 +283,28 @@ def main(db, json_path):
# CCP still has 5 subsystems assigned to T3Cs, even though only 4 are available / usable. They probably have some
# old legacy requirement or assumption that makes it difficult for them to change this value in the data. But for
# pyfa, we can do it here as a post-processing step
eos.db.gamedata_engine.execute("UPDATE dgmtypeattribs SET value = 4.0 WHERE attributeID = ?", (1367,))
eos.db.gamedata_engine.execute('UPDATE dgmtypeattribs SET value = 4.0 WHERE attributeID = ?', (1367,))
eos.db.gamedata_engine.execute("UPDATE invtypes SET published = 0 WHERE typeName LIKE '%abyssal%'")
eos.db.gamedata_engine.execute('UPDATE invtypes SET published = 0 WHERE typeName LIKE \'%abyssal%\'')
# fix for #1722 until CCP gets their shit together
eos.db.gamedata_engine.execute('UPDATE invtypes SET typeName = \'Small Abyssal Energy Nosferatu\' WHERE typeID = ? AND typeName = ?', (48419, ''))
print()
for x in CATEGORIES_TO_REMOVE:
cat = eos.db.gamedata_session.query(eos.gamedata.Category).filter(eos.gamedata.Category.ID == x).first()
print ("Removing Category: {}".format(cat.name))
print ('Removing Category: {}'.format(cat.name))
eos.db.gamedata_session.delete(cat)
eos.db.gamedata_session.commit()
eos.db.gamedata_engine.execute("VACUUM")
eos.db.gamedata_engine.execute('VACUUM')
print("done")
print('done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="This scripts dumps effects from an sqlite cache dump to mongo")
parser.add_argument("-d", "--db", required=True, type=str, help="The sqlalchemy connectionstring, example: sqlite:///c:/tq.db")
parser.add_argument("-j", "--json", required=True, type=str, help="The path to the json dump")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='This scripts dumps effects from an sqlite cache dump to mongo')
parser.add_argument('-d', '--db', required=True, type=str, help='The sqlalchemy connectionstring, example: sqlite:///c:/tq.db')
parser.add_argument('-j', '--json', required=True, type=str, help='The path to the json dump')
args = parser.parse_args()
main(args.db, args.json)

View File

@@ -187,11 +187,11 @@ class Fit(object):
# error during the command loop
refreshFits = set()
for projection in list(fit.projectedOnto.values()):
if projection.victim_fit != fit and projection.victim_fit in eos.db.saveddata_session: # GH issue #359
if projection.victim_fit and projection.victim_fit != fit and projection.victim_fit in eos.db.saveddata_session: # GH issue #359
refreshFits.add(projection.victim_fit)
for booster in list(fit.boostedOnto.values()):
if booster.boosted_fit != fit and booster.boosted_fit in eos.db.saveddata_session: # GH issue #359
if booster.boosted_fit and booster.boosted_fit != fit and booster.boosted_fit in eos.db.saveddata_session: # GH issue #359
refreshFits.add(booster.boosted_fit)
eos.db.remove(fit)
@@ -626,12 +626,11 @@ class Fit(object):
def changeModule(self, fitID, position, newItemID):
fit = eos.db.getFit(fitID)
module = fit.modules[position]
# We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot.
if self.isAmmo(newItemID):
module = fit.modules[position]
if not module.isEmpty:
self.setAmmo(fitID, newItemID, [module])
if self.isAmmo(newItemID) and not module.isEmpty:
self.setAmmo(fitID, newItemID, [module])
return True
pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", position, fitID)
@@ -640,14 +639,17 @@ class Fit(object):
# Dummy it out in case the next bit fails
fit.modules.toDummy(position)
ret = None
try:
m = es_Module(item)
except ValueError:
pyfalog.warning("Invalid item: {0}", newItemID)
return False
if m.fits(fit):
if m.slot != module.slot:
fit.modules.toModule(position, module)
# Fits, but we selected wrong slot type, so don't want to overwrite because we will append on failure (none)
ret = None
elif m.fits(fit):
m.owner = fit
fit.modules.toModule(position, m)
if m.isValidState(State.ACTIVE):
@@ -661,9 +663,8 @@ class Fit(object):
fit.fill()
eos.db.commit()
return True
else:
return None
ret = True
return ret
def moveCargoToModule(self, fitID, moduleIdx, cargoIdx, copyMod=False):
"""

View File

@@ -277,15 +277,8 @@ class Market(object):
# Dictionary of items with forced market group (service assumes they have no
# market group assigned in db, otherwise they'll appear in both original and forced groups)
self.ITEMS_FORCEDMARKETGROUP = {
"Advanced Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Civilian Damage Control" : 615, # Ship Equipment > Hull & Armor > Damage Controls
"Civilian EM Ward Field" : 1695,
# Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners
"Civilian Explosive Deflection Field" : 1694,
# Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners
"Advanced Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
"Civilian Hobgoblin" : 837, # Drones > Combat Drones > Light Scout Drones
"Civilian Kinetic Deflection Field" : 1693,
# Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners
"Civilian Light Missile Launcher" : 640,
# Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers
"Civilian Scourge Light Missile" : 920,
@@ -293,8 +286,6 @@ class Market(object):
"Civilian Small Remote Armor Repairer" : 1059,
# Ship Equipment > Hull & Armor > Remote Armor Repairers > Small
"Civilian Small Remote Shield Booster" : 603, # Ship Equipment > Shield > Remote Shield Boosters > Small
"Civilian Stasis Webifier" : 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers
"Civilian Warp Disruptor" : 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors
"Hardwiring - Zainou 'Sharpshooter' ZMX10" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX100" : 1493,
@@ -307,11 +298,9 @@ class Market(object):
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Nugoehuvi Synth Blue Pill Booster" : 977, # Implants & Boosters > Booster
"Prototype Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Prototype Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
"Prototype Iris Probe Launcher" : 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers
"Shadow" : 1310, # Drones > Combat Drones > Fighter Bombers
"Standard Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Standard Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
}
self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP)
@@ -538,7 +527,7 @@ class Market(object):
categories = ['Drone', 'Fighter', 'Implant']
for item in items:
if item.category.ID == 20: # Implants and Boosters
if item.category.ID == 20 and item.group.ID != 303: # Implants not Boosters
implant_remove_list = set()
implant_remove_list.add("Low-Grade ")
implant_remove_list.add("Low-grade ")
@@ -552,15 +541,6 @@ class Market(object):
implant_remove_list.add(" - Elite")
implant_remove_list.add(" - Improved")
implant_remove_list.add(" - Standard")
implant_remove_list.add("Copper ")
implant_remove_list.add("Gold ")
implant_remove_list.add("Silver ")
implant_remove_list.add("Advanced ")
implant_remove_list.add("Improved ")
implant_remove_list.add("Prototype ")
implant_remove_list.add("Standard ")
implant_remove_list.add("Strong ")
implant_remove_list.add("Synth ")
for implant_prefix in ("-6", "-7", "-8", "-9", "-10"):
for i in range(50):
@@ -596,6 +576,16 @@ class Market(object):
if trimmed_variations_list:
variations_list = trimmed_variations_list
# If the items are boosters then filter variations to only include boosters for the same slot.
BOOSTER_GROUP_ID = 303
if all(map(lambda i: i.group.ID == BOOSTER_GROUP_ID, items)) and len(items) > 0:
# 'boosterness' is the database's attribute name for Booster Slot
reqSlot = next(items.__iter__()).getAttribute('boosterness')
# If the item and it's variation both have a marketGroupID it should match for the variation to be considered valid.
marketGroupID = [next(filter(None, map(lambda i: i.marketGroupID, items)), None), None]
matchSlotAndMktGrpID = lambda v: v.getAttribute('boosterness') == reqSlot and v.marketGroupID in marketGroupID
variations_list = list(filter(matchSlotAndMktGrpID, variations_list))
variations.update(variations_list)
return variations
@@ -657,6 +647,12 @@ class Market(object):
def marketGroupHasTypesCheck(self, mg):
"""If market group has any items, return true"""
if mg and mg.ID in self.ITEMS_FORCEDMARKETGROUP_R:
# This shouldn't occur normally but makes errors more mild when ITEMS_FORCEDMARKETGROUP is outdated.
if len(mg.children) > 0 and len(mg.items) == 0:
pyfalog.error(("Market group \"{0}\" contains no items and has children. "
"ITEMS_FORCEDMARKETGROUP is likely outdated and will need to be "
"updated for {1} to display correctly.").format(mg, self.ITEMS_FORCEDMARKETGROUP_R[mg.ID]))
return False
return True
elif len(mg.items) > 0:
return True

View File

@@ -24,7 +24,6 @@ from logbook import Logger
import collections
import json
import threading
import locale
from bs4 import UnicodeDammit
@@ -33,6 +32,7 @@ from codecs import open
import xml.parsers.expat
from eos import db
from eos.db.gamedata.queries import getAttributeInfo
from service.fit import Fit as svcFit
# noinspection PyPackageRequirements
@@ -1064,85 +1064,104 @@ class Port(object):
return fit_list
@staticmethod
def _exportEftBase(fit):
"""Basically EFT format does not require blank lines
also, it's OK to arrange modules randomly?
"""
offineSuffix = " /OFFLINE"
export = "[%s, %s]\n" % (fit.ship.item.name, fit.name)
stuff = {}
@classmethod
def exportEft(cls, fit, mutations=False, implants=False):
# EFT formatted export is split in several sections, each section is
# separated from another using 2 blank lines. Sections might have several
# sub-sections, which are separated by 1 blank line
sections = []
header = '[{}, {}]'.format(fit.ship.item.name, fit.name)
# Section 1: modules, rigs, subsystems, services
def formatAttrVal(val):
if int(val) == val:
return int(val)
return val
offineSuffix = ' /OFFLINE'
modsBySlotType = {}
sFit = svcFit.getInstance()
for module in fit.modules:
slot = module.slot
if slot not in stuff:
stuff[slot] = []
curr = module.item.name if module.item \
else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "")
if module.charge and sFit.serviceFittingOptions["exportCharges"]:
curr += ", %s" % module.charge.name
if module.state == State.OFFLINE:
curr += offineSuffix
curr += "\n"
stuff[slot].append(curr)
slotTypeMods = modsBySlotType.setdefault(slot, [])
if module.item:
mutatedMod = bool(module.mutators)
# if module was mutated, use base item name for export
if mutatedMod:
modName = module.baseItem.name
else:
modName = module.item.name
modOfflineSuffix = offineSuffix if module.state == State.OFFLINE else ''
if module.charge and sFit.serviceFittingOptions['exportCharges']:
slotTypeMods.append('{}, {}{}'.format(modName, module.charge.name, modOfflineSuffix))
else:
slotTypeMods.append('{}{}'.format(modName, modOfflineSuffix))
if mutatedMod and mutations:
mutationGrade = module.mutaplasmid.item.name.split(' ', 1)[0].lower()
mutatedAttrs = {}
for attrID, mutator in module.mutators.items():
attrName = getAttributeInfo(attrID).name
mutatedAttrs[attrName] = mutator.value
customAttrsLine = ', '.join('{} {}'.format(a, formatAttrVal(mutatedAttrs[a])) for a in sorted(mutatedAttrs))
slotTypeMods.append(' {}: {}'.format(mutationGrade, customAttrsLine))
else:
slotTypeMods.append('[Empty {} slot]'.format(Slot.getName(slot).capitalize() if slot is not None else ''))
modSection = []
for slotType in EFT_SLOT_ORDER:
data = stuff.get(slotType)
if data is not None:
export += "\n"
for curr in data:
export += curr
rackLines = []
data = modsBySlotType.get(slotType, ())
for line in data:
rackLines.append(line)
if rackLines:
modSection.append('\n'.join(rackLines))
if modSection:
sections.append('\n\n'.join(modSection))
if len(fit.drones) > 0:
export += "\n\n"
for drone in fit.drones:
export += "%s x%s\n" % (drone.item.name, drone.amount)
# Section 2: drones, fighters
minionSection = []
droneLines = []
for drone in sorted(fit.drones, key=lambda d: d.item.name):
droneLines.append('{} x{}'.format(drone.item.name, drone.amount))
if droneLines:
minionSection.append('\n'.join(droneLines))
fighterLines = []
for fighter in sorted(fit.fighters, key=lambda f: f.item.name):
fighterLines.append('{} x{}'.format(fighter.item.name, fighter.amountActive))
if fighterLines:
minionSection.append('\n'.join(fighterLines))
if minionSection:
sections.append('\n\n'.join(minionSection))
if len(fit.fighters) > 0:
export += "\n\n"
for fighter in fit.fighters:
export += "%s x%s\n" % (fighter.item.name, fighter.amountActive)
# Section 3: implants, boosters
if implants:
charSection = []
implantLines = []
for implant in fit.implants:
implantLines.append(implant.item.name)
if implantLines:
charSection.append('\n'.join(implantLines))
boosterLines = []
for booster in fit.boosters:
boosterLines.append(booster.item.name)
if boosterLines:
charSection.append('\n'.join(boosterLines))
if charSection:
sections.append('\n\n'.join(charSection))
if export[-1] == "\n":
export = export[:-1]
# Section 4: cargo
cargoLines = []
for cargo in sorted(fit.cargo, key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name)):
cargoLines.append('{} x{}'.format(cargo.item.name, cargo.amount))
if cargoLines:
sections.append('\n'.join(cargoLines))
return export
@classmethod
def exportEft(cls, fit):
export = cls._exportEftBase(fit)
if len(fit.cargo) > 0:
export += "\n\n\n"
for cargo in fit.cargo:
export += "%s x%s\n" % (cargo.item.name, cargo.amount)
if export[-1] == "\n":
export = export[:-1]
return export
return '{}\n\n{}'.format(header, '\n\n\n'.join(sections))
@classmethod
def exportEftImps(cls, fit):
export = cls._exportEftBase(fit)
if len(fit.implants) > 0 or len(fit.boosters) > 0:
export += "\n\n\n"
for implant in fit.implants:
export += "%s\n" % implant.item.name
for booster in fit.boosters:
export += "%s\n" % booster.item.name
if export[-1] == "\n":
export = export[:-1]
if len(fit.cargo) > 0:
export += "\n\n\n"
for cargo in fit.cargo:
export += "%s x%s\n" % (cargo.item.name, cargo.amount)
if export[-1] == "\n":
export = export[:-1]
return export
return cls.exportEft(fit, implants=True)
@staticmethod
def exportDna(fit):

View File

@@ -228,10 +228,11 @@ class PriceWorkerThread(threading.Thread):
def trigger(self, prices, callbacks):
self.queue.put((callbacks, prices))
def setToWait(self, itemID, callback):
if itemID not in self.wait:
self.wait[itemID] = []
self.wait[itemID].append(callback)
def setToWait(self, prices, callback):
for x in prices:
if x.typeID not in self.wait:
self.wait[x.typeID] = []
self.wait[x.typeID].append(callback)
from service.marketSources import evemarketer, evemarketdata # noqa: E402

View File

@@ -248,6 +248,11 @@ class NetworkSettings(object):
proto = "{0}://".format(prefix)
if proxyline[:len(proto)] == proto:
proxyline = proxyline[len(proto):]
# sometimes proxyline contains "user:password@" section before proxy address
# remove it if present, so later split by ":" works
if '@' in proxyline:
userPass, proxyline = proxyline.split("@")
# TODO: do something with user/password?
proxAddr, proxPort = proxyline.split(":")
proxPort = int(proxPort.rstrip("/"))
proxy = (proxAddr, proxPort)