Merge branch 'development' into More_Variations

Conflicts:
	gui/builtinContextMenus/metaSwap.py
This commit is contained in:
blitzmann
2017-02-26 02:24:38 -05:00
416 changed files with 17820 additions and 9680 deletions

26
.codecov.yml Normal file
View File

@@ -0,0 +1,26 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project: yes
patch: yes
changes: no
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "header, diff"
behavior: default
require_changes: no

40
.gitattributes vendored Normal file
View File

@@ -0,0 +1,40 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
# *.c text
# *.h text
# Declare files that will always have CRLF line endings on checkout.
# Source files
# ============
*.pxd text eol=crlf
*.py text eol=crlf
*.py3 text eol=crlf
*.pyw text eol=crlf
*.pyx text eol=crlf
pyfa.py text eol=lf
# Denote all files that are truly binary and should not be modified.
# Binary files
# ============
*.db binary
*.p binary
*.pkl binary
*.pyc binary
*.pyd binary
*.pyo binary
# Note: .db, .p, and .pkl files are associated
# with the python modules ``pickle``, ``dbm.*``,
# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb``
# (among others).
# Denote all files that are truly binary and should not be modified.
# Image files
# ============
*.png binary
*.jpg binary
*.icns binary
*.ico binary

1
.gitignore vendored
View File

@@ -49,7 +49,6 @@ Pyfa.egg-info/
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt

25
.travis.yml Normal file
View File

@@ -0,0 +1,25 @@
language: python
python:
- '2.7'
env:
- TOXENV=pep8
addons:
apt:
packages:
# for wxPython:
- python-wxgtk2.8
- python-wxtools
- wx2.8-doc
- wx2.8-examples
- wx2.8-headers
- wx2.8-i18n
before_install:
- pip install -U tox
install:
- pip install -r requirements.txt
- pip install -r requirements_test.txt
script:
- tox
- py.test --cov=./
after_success:
- bash <(curl -s https://codecov.io/bash)

32
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,32 @@
# Submit a bug report bug report or feature request
Here you can inform pyfa developers of potential bugs or suggest features / improvements to the project. Please check
to make sure that the bug hasn't been reported or feature requested before submitting. If you have general questions
about the project and want to reach out to the developers personally, please check out out our [Slack]
(https://pyfainvite.azurewebsites.net/).
---
## Bug Report
### Expected behavior:
### Actual behavior:
### Detailed steps to reproduce:
### Fits involved in EFT format (Edit > To Clipboard > EFT):
### Release or development git branch? Please note the release version or commit hash:
### Operating system and version (eg: Windows 10, OS X 10.9, OS X 10.11, Ubuntu 16.10):
### Other relevant information:

View File

@@ -1,6 +1,6 @@
# pyfa
[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/)
[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/) [![Build Status](https://travis-ci.org/pyfa-org/Pyfa.svg?branch=master)](https://travis-ci.org/pyfa-org/Pyfa)
![pyfa](https://cloud.githubusercontent.com/assets/3904767/10271512/af385ef2-6ade-11e5-8f67-52b8b1e4c797.PNG)
@@ -38,7 +38,7 @@ If you wish to help with development or simply need to run pyfa through a Python
* Python 2.7
* `wxPython` 2.8/3.0
* `sqlalchemy` >= 0.6
* `sqlalchemy` >= 1.0.5
* `dateutil`
* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`)
* `requests`
@@ -51,8 +51,7 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE
## Resources
* Development repository: [https://github.com/pyfa-org/Pyfa](https://github.com/pyfa-org/Pyfa)
* XMPP conference: [pyfa@conference.jabber.org](pyfa@conference.jabber.org)
* [EVE forum thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609)
* [EVE forum thread](https://forums.eveonline.com/default.aspx?g=posts&t=466425)
* [EVE University guide using pyfa](http://wiki.eveuniversity.org/Guide_to_using_PYFA)
* [EVE Online website](http://www.eveonline.com/)

View File

@@ -0,0 +1,13 @@
<code_scheme name="Pyfa">
<option name="LINE_SEPARATOR" value="&#xD;&#xA;" />
<option name="RIGHT_MARGIN" value="165" />
<Python>
<option name="NEW_LINE_AFTER_COLON" value="true" />
<option name="DICT_ALIGNMENT" value="2" />
<option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" />
<option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" />
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />
<option name="OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS" value="true" />
<option name="OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE" value="true" />
</Python>
</code_scheme>

View File

@@ -0,0 +1,54 @@
<profile version="1.0">
<option name="myName" value="Pyfa" />
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="2.7" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="wxPython" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E203" />
<option value="E127" />
<option value="E128" />
<option value="E126" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
<option value="N803" />
<option value="N814" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>

View File

@@ -1,27 +1,28 @@
import os
import sys
# TODO: move all logging back to pyfa.py main loop
# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it)
import logging
import logging.handlers
from logbook import Logger
pyfalog = Logger(__name__)
# Load variable overrides specific to distribution type
try:
import configforced
except ImportError:
pyfalog.warning("Failed to import: configforced")
configforced = None
# Turns on debug mode
debug = False
# Defines if our saveddata will be in pyfa root or not
saveInRoot = False
# Version data
version = "1.26.1"
version = "1.27.3"
tag = "git"
expansionName = "YC118.10"
expansionVersion = "1.2"
expansionName = "YC119.2"
expansionVersion = "1.4"
evemonMinVersion = "4081"
pyfaPath = None
@@ -30,35 +31,28 @@ saveDB = None
gameDB = None
class StreamToLogger(object):
"""
Fake file-like stream object that redirects writes to a logger instance.
From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/
"""
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''
def write(self, buf):
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())
def isFrozen():
if hasattr(sys, 'frozen'):
return True
else:
return False
def __createDirs(path):
if not os.path.exists(path):
os.makedirs(path)
def getPyfaRoot():
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0]
root = os.path.dirname(os.path.realpath(os.path.abspath(base)))
root = unicode(root, sys.getfilesystemencoding())
return root
def __createDirs(path):
if not os.path.exists(path):
os.makedirs(path)
def getDefaultSave():
return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding())
def defPaths(customSavePath):
global debug
@@ -68,10 +62,7 @@ def defPaths(customSavePath):
global gameDB
global saveInRoot
if debug:
logLevel = logging.DEBUG
else:
logLevel = logging.WARN
pyfalog.debug("Configuring Pyfa")
# The main pyfa directory which contains run.py
# Python 2.X uses ANSI by default, so we need to convert the character encoding
@@ -87,36 +78,16 @@ def defPaths(customSavePath):
else:
savePath = getattr(configforced, "savePath", None)
if savePath is None:
if customSavePath is None: # customSavePath is not overriden
savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")),
sys.getfilesystemencoding())
if customSavePath is None: # customSavePath is not overriden
savePath = getDefaultSave()
else:
savePath = customSavePath
__createDirs(savePath)
if isFrozen():
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem")
os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem")
format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s'
logging.basicConfig(format=format, level=logLevel)
handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3)
formatter = logging.Formatter(format)
handler.setFormatter(formatter)
logging.getLogger('').addHandler(handler)
logging.info("Starting pyfa")
if hasattr(sys, 'frozen'):
stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
# This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this.
#stderr_logger = logging.getLogger('STDERR')
#sl = StreamToLogger(stderr_logger, logging.ERROR)
#sys.stderr = sl
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8')
os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8')
# The database where we store all the fits etc
saveDB = os.path.join(savePath, "saveddata.db")
@@ -126,10 +97,10 @@ def defPaths(customSavePath):
# maintenance script
gameDB = os.path.join(pyfaPath, "eve.db")
## DON'T MODIFY ANYTHING BELOW ##
# DON'T MODIFY ANYTHING BELOW
import eos.config
#Caching modifiers, disable all gamedata caching, its unneeded.
# Caching modifiers, disable all gamedata caching, its unneeded.
eos.config.gamedataCache = False
# saveddata db location modifier, shouldn't ever need to touch this
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"

5666
dist_assets/cacert.pem Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,2 @@
version = "0.2.3"
tag = "git"
def test():
import tests.runTests
import unittest
unittest.main(defaultTest="discover", testLoader=tests.runTests.loader)
version = "0.2.3"
tag = "git"

View File

@@ -4,10 +4,12 @@ from os.path import realpath, join, dirname, abspath
debug = False
gamedataCache = True
saveddataCache = True
gamedata_version = ""
gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")),
sys.getfilesystemencoding())
saveddata_connectionstring = 'sqlite:///' + unicode(
realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
# Autodetect path, only change if the autodetection bugs out.
path = dirname(unicode(__file__, sys.getfilesystemencoding()))

View File

@@ -20,13 +20,13 @@
import threading
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import pool
from sqlalchemy.orm import sessionmaker
import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
class ReadOnlyException(Exception):
@@ -49,7 +49,9 @@ try:
config.gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
except:
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)
config.gamedata_version = None
saveddata_connectionstring = config.saveddata_connectionstring
@@ -62,16 +64,22 @@ if saveddata_connectionstring is not None:
saveddata_meta = MetaData()
saveddata_meta.bind = saveddata_engine
saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)()
else:
saveddata_meta = None
# Lock controlling any changes introduced to session
sd_lock = threading.Lock()
# Import all the definitions for all our database stuff
from eos.db.gamedata import *
from eos.db.saveddata import *
# noinspection PyPep8
from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, miscData, module, override, price, queries, skill, targetResists, user
# Import queries
# noinspection PyPep8
from eos.db.gamedata.queries import *
# noinspection PyPep8
from eos.db.saveddata.queries import *
# If using in memory saveddata, you'll want to reflect it so the data structure is good.

View File

@@ -1,2 +1,2 @@
__all__ = ["attribute", "category", "effect", "group", "metaData",
"icon", "item", "marketGroup", "metaGroup", "unit"]
"icon", "item", "marketGroup", "metaGroup", "unit", "alphaClones"]

View File

@@ -0,0 +1,50 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Column, String, Integer, Table, ForeignKey
from sqlalchemy.orm import relation, mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import AlphaClone, AlphaCloneSkill
alphaclones_table = Table(
"alphaClones",
gamedata_meta,
Column("alphaCloneID", Integer, primary_key=True),
Column("alphaCloneName", String),
)
alphacloneskskills_table = Table(
"alphaCloneSkills",
gamedata_meta,
Column("alphaCloneID", Integer, ForeignKey("alphaClones.alphaCloneID"), primary_key=True),
Column("typeID", Integer, primary_key=True),
Column("level", Integer),
)
mapper(AlphaClone, alphaclones_table,
properties={
"ID": synonym("alphaCloneID"),
"skills": relation(
AlphaCloneSkill,
cascade="all,delete-orphan",
backref="clone")
})
mapper(AlphaCloneSkill, alphacloneskskills_table)

View File

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Attribute, Icon, AttributeInfo, Unit
from eos.gamedata import Attribute, AttributeInfo, Unit, Icon
typeattributes_table = Table("dgmtypeattribs", gamedata_meta,
Column("value", Float),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Category, Icon
from eos.gamedata import Category, Icon
categories_table = Table("invcategories", gamedata_meta,
Column("categoryID", Integer, primary_key=True),

View File

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import mapper, synonym, relation, deferred
from eos.db import gamedata_meta
from eos.types import Effect, EffectInfo
from eos.gamedata import Effect, EffectInfo
typeeffects_table = Table("dgmtypeeffects", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Group, Icon, Category
from eos.gamedata import Category, Group, Icon
groups_table = Table("invgroups", gamedata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Table
from sqlalchemy.orm import mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Icon
from eos.gamedata import Icon
icons_table = Table("icons", gamedata_meta,
Column("iconID", Integer, primary_key=True),

View File

@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
from sqlalchemy.orm.collections import attribute_mapped_collection
from eos.db import gamedata_meta
from eos.types import Icon, Attribute, Item, Effect, MetaType, Group, Traits
from eos.gamedata import Attribute, Effect, Group, Icon, Item, MetaType, Traits
items_table = Table("invtypes", gamedata_meta,
Column("typeID", Integer, primary_key=True),
@@ -39,8 +39,8 @@ items_table = Table("invtypes", gamedata_meta,
Column("iconID", Integer, ForeignKey("icons.iconID")),
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True))
from .metaGroup import metatypes_table
from .traits import traits_table
from .metaGroup import metatypes_table # noqa
from .traits import traits_table # noqa
mapper(Item, items_table,
properties={"group": relation(Group, backref="items"),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Item, MarketGroup, Icon
from eos.gamedata import Icon, Item, MarketGroup
marketgroups_table = Table("invmarketgroups", gamedata_meta,
Column("marketGroupID", Integer, primary_key=True),

View File

@@ -1,30 +1,30 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Column, Table, String
from sqlalchemy.orm import mapper
from eos.db import gamedata_meta
from eos.types import MetaData
metadata_table = Table("metadata", gamedata_meta,
Column("field_name", String, primary_key=True),
Column("field_value", String))
mapper(MetaData, metadata_table)
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Column, Table, String
from sqlalchemy.orm import mapper
from eos.db import gamedata_meta
from eos.gamedata import MetaData
metadata_table = Table("metadata", gamedata_meta,
Column("field_name", String, primary_key=True),
Column("field_value", String))
mapper(MetaData, metadata_table)

View File

@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym
from eos.db import gamedata_meta
from eos.db.gamedata.item import items_table
from eos.types import MetaGroup, Item, MetaType
from eos.gamedata import Item, MetaGroup, MetaType
metagroups_table = Table("invmetagroups", gamedata_meta,
Column("metaGroupID", Integer, primary_key=True),

View File

@@ -25,7 +25,7 @@ from eos.db import gamedata_session
from eos.db.gamedata.metaGroup import metatypes_table, items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
from eos.types import Item, Category, Group, MarketGroup, AttributeInfo, MetaData, MetaGroup
from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData
configVal = getattr(eos.config, "gamedataCache", None)
if configVal is True:
@@ -98,6 +98,24 @@ def getItem(lookfor, eager=None):
return item
@cachedQuery(1, "lookfor")
def getAlphaClone(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
item = gamedata_session.query(AlphaClone).get(lookfor)
else:
item = gamedata_session.query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
return item
def getAlphaCloneList(eager=None):
eager = processEager(eager)
clones = gamedata_session.query(AlphaClone).options(*eager).all()
return clones
groupNameMap = {}
@@ -292,9 +310,9 @@ def directAttributeRequest(itemIDs, attrIDs):
if not isinstance(itemID, int):
raise TypeError("All itemIDs must be integer")
q = select((eos.types.Item.typeID, eos.types.Attribute.attributeID, eos.types.Attribute.value),
and_(eos.types.Attribute.attributeID.in_(attrIDs), eos.types.Item.typeID.in_(itemIDs)),
from_obj=[join(eos.types.Attribute, eos.types.Item)])
q = select((Item.typeID, Attribute.attributeID, Attribute.value),
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
from_obj=[join(Attribute, Item)])
result = gamedata_session.execute(q).fetchall()
return result

View File

@@ -2,7 +2,7 @@ from sqlalchemy import Column, Table, Integer, String, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import gamedata_meta
from eos.types import Traits
from eos.gamedata import Traits
traits_table = Table("invtraits", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, Integer, String
from sqlalchemy.orm import mapper, synonym
from eos.db import gamedata_meta
from eos.types import Unit
from eos.gamedata import Unit
groups_table = Table("dgmunits", gamedata_meta,
Column("unitID", Integer, primary_key=True),

View File

@@ -1,11 +1,11 @@
import logging
from logbook import Logger
import shutil
import time
import config
import migrations
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
def getVersion(db):
@@ -37,7 +37,7 @@ def update(saveddata_engine):
for version in xrange(dbVersion, appVersion):
func = migrations.updates[version + 1]
if func:
logger.info("Applying database update: %d", version + 1)
pyfalog.info("Applying database update: {0}", version + 1)
func(saveddata_engine)
# when all is said and done, set version to current

View File

@@ -45,7 +45,7 @@ CONVERSIONS = {
8746, # Quantum Co-Processor
8745, # Photonic CPU Enhancer
15425, # Naiyon's Modified Co-Processor (never existed but convert
# anyway as some fits may include it)
# anyway as some fits may include it)
],
8748: [ # Upgraded Co-Processor
8747, # Nanomechanical CPU Enhancer I
@@ -70,7 +70,7 @@ CONVERSIONS = {
16543, # Micro 'Vigor' Core Augmentation
],
8089: [ # Compact Light Missile Launcher
8093, # Prototype 'Arbalest' Light Missile Launcher
8093, # Prototype 'Arbalest' Light Missile Launcher
],
8091: [ # Ample Light Missile Launcher
7993, # Experimental TE-2100 Light Missile Launcher
@@ -82,6 +82,7 @@ CONVERSIONS = {
]
}
def upgrade(saveddata_engine):
# Update fits schema to include target resists attribute
try:
@@ -92,6 +93,7 @@ def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -6,6 +6,7 @@ Migration 10
import sqlalchemy
def upgrade(saveddata_engine):
# Update projectedFits schema to include active attribute
try:

View File

@@ -7,7 +7,6 @@ Migration 11
modules with their new replacements
"""
CONVERSIONS = {
16467: ( # Medium Gremlin Compact Energy Neutralizer
16471, # Medium Unstable Power Fluctuator I
@@ -106,11 +105,12 @@ CONVERSIONS = {
),
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -7,7 +7,6 @@ Migration 12
modules with their new replacements
"""
CONVERSIONS = {
16457: ( # Crosslink Compact Ballistic Control System
16459, # Muon Coil Bolt Array I
@@ -330,11 +329,12 @@ CONVERSIONS = {
),
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -6,10 +6,11 @@ Migration 13
import sqlalchemy
def upgrade(saveddata_engine):
# Update fits schema to include implant location attribute
try:
saveddata_engine.execute("SELECT implantLocation FROM fits LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN implantLocation INTEGER;")
saveddata_engine.execute("UPDATE fits SET implantLocation = 0")
saveddata_engine.execute("UPDATE fits SET implantLocation = 0")

View File

@@ -6,8 +6,10 @@ Migration 14
import sqlalchemy
def upgrade(saveddata_engine):
if saveddata_engine.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters':
if saveddata_engine.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters':
# Fighters table exists
try:
saveddata_engine.execute("SELECT active FROM fighters LIMIT 1")
@@ -16,4 +18,4 @@ def upgrade(saveddata_engine):
# (they will be recreated)
saveddata_engine.execute("DROP TABLE fighters")
saveddata_engine.execute("DROP TABLE fightersAbilities")
saveddata_engine.execute("DROP TABLE fightersAbilities")

View File

@@ -4,10 +4,8 @@ Migration 15
- Delete projected modules on citadels
"""
import sqlalchemy
def upgrade(saveddata_engine):
sql = """
DELETE FROM modules WHERE ID IN
(

View File

@@ -6,6 +6,7 @@ Migration 16
import sqlalchemy
def upgrade(saveddata_engine):
# Update fits schema to include notes attribute
try:

View File

@@ -4,8 +4,6 @@ Migration 17
- Moves all fleet boosters to the new schema
"""
import sqlalchemy
def upgrade(saveddata_engine):
from eos.db import saveddata_session
@@ -33,7 +31,8 @@ def upgrade(saveddata_engine):
inserts.append({"boosterID": value, "boostedID": boosted, "active": 1})
try:
saveddata_session.execute(commandFits_table.insert(), {"boosterID": value, "boostedID": boosted, "active": 1})
except Exception, e:
saveddata_session.execute(commandFits_table.insert(),
{"boosterID": value, "boostedID": boosted, "active": 1})
except Exception:
pass
saveddata_session.commit()

View File

@@ -4,27 +4,26 @@ Migration 8
- Converts modules from old Warfare Links to Command Modules
"""
CONVERSIONS = {
42526: ( # Armor Command Burst I
20069, # Armored Warfare Link - Damage Control I
20409, # Armored Warfare Link - Passive Defense I
22227, # Armored Warfare Link - Rapid Repair I
20069, # Armored Warfare Link - Damage Control I
20409, # Armored Warfare Link - Passive Defense I
22227, # Armored Warfare Link - Rapid Repair I
),
43552: ( # Armor Command Burst II
4264, # Armored Warfare Link - Damage Control II
4266, # Armored Warfare Link - Passive Defense II
4266, # Armored Warfare Link - Rapid Repair II
4264, # Armored Warfare Link - Damage Control II
4266, # Armored Warfare Link - Passive Defense II
4266, # Armored Warfare Link - Rapid Repair II
),
42527: ( # Information Command Burst I
11052, # Information Warfare Link - Sensor Integrity I
20405, # Information Warfare Link - Recon Operation I
20406, # Information Warfare Link - Electronic Superiority I
11052, # Information Warfare Link - Sensor Integrity I
20405, # Information Warfare Link - Recon Operation I
20406, # Information Warfare Link - Electronic Superiority I
),
43554: ( # Information Command Burst II
4268, # Information Warfare Link - Electronic Superiority II
4270, # Information Warfare Link - Recon Operation II
4272, # Information Warfare Link - Sensor Integrity II
4268, # Information Warfare Link - Electronic Superiority II
4270, # Information Warfare Link - Recon Operation II
4272, # Information Warfare Link - Sensor Integrity II
),
42529: ( # Shield Command Burst I
20124, # Siege Warfare Link - Active Shielding I
@@ -34,17 +33,17 @@ CONVERSIONS = {
43555: ( # Shield Command Burst II
4280, # Siege Warfare Link - Active Shielding II
4282, # Siege Warfare Link - Shield Efficiency II
4284 # Siege Warfare Link - Shield Harmonizing II
4284 # Siege Warfare Link - Shield Harmonizing II
),
42530: ( # Skirmish Command Burst I
11017, # Skirmish Warfare Link - Interdiction Maneuvers I
20070, # Skirmish Warfare Link - Evasive Maneuvers I
20408, # Skirmish Warfare Link - Rapid Deployment I
11017, # Skirmish Warfare Link - Interdiction Maneuvers I
20070, # Skirmish Warfare Link - Evasive Maneuvers I
20408, # Skirmish Warfare Link - Rapid Deployment I
),
43556: ( # Skirmish Command Burst II
4286, # Skirmish Warfare Link - Evasive Maneuvers II
4288, # Skirmish Warfare Link - Interdiction Maneuvers II
4290 # Skirmish Warfare Link - Rapid Deployment II
4290 # Skirmish Warfare Link - Rapid Deployment II
),
42528: ( # Mining Foreman Burst I
22553, # Mining Foreman Link - Harvester Capacitor Efficiency I
@@ -54,15 +53,16 @@ CONVERSIONS = {
43551: ( # Mining Foreman Burst II
4274, # Mining Foreman Link - Harvester Capacitor Efficiency II
4276, # Mining Foreman Link - Laser Optimization II
4278 # Mining Foreman Link - Mining Laser Field Enhancement II
4278 # Mining Foreman Link - Mining Laser Field Enhancement II
),
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -4,17 +4,15 @@ Migration 19
- Deletes broken references to fits from the commandFits table (see GH issue #844)
"""
import sqlalchemy
def upgrade(saveddata_engine):
from eos.db import saveddata_session
sql = """
DELETE FROM commandFits
WHERE boosterID NOT IN (select ID from fits)
OR boostedID NOT IN (select ID from fits)
"""
DELETE FROM commandFits
WHERE boosterID NOT IN (select ID from fits)
OR boostedID NOT IN (select ID from fits)
"""
saveddata_session.execute(sql)
saveddata_session.commit()

View File

@@ -6,6 +6,7 @@ Migration 2
import sqlalchemy
def upgrade(saveddata_engine):
# Update characters schema to include default chars
try:

View File

@@ -0,0 +1,15 @@
"""
Migration 20
- Adds support for alpha clones to the characters table
"""
import sqlalchemy
def upgrade(saveddata_engine):
# Update characters schema to include alphaCloneID
try:
saveddata_engine.execute("SELECT alphaCloneID FROM characters LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE characters ADD COLUMN alphaCloneID INTEGER;")

View File

@@ -0,0 +1,10 @@
"""
Migration 21
- Fixes discrepancy in drone table where we may have an amount active that is not equal to the amount in the stack
(we don't support activating only 2/5 drones). See GH issue #728
"""
def upgrade(saveddata_engine):
saveddata_engine.execute("UPDATE drones SET amountActive = amount where amountActive > 0 AND amountActive <> amount;")

View File

@@ -6,6 +6,7 @@ Migration 3
import sqlalchemy
def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT modeID FROM fits LIMIT 1")

View File

@@ -10,7 +10,6 @@ Migration 4
and output of itemDiff.py
"""
CONVERSIONS = {
506: ( # 'Basic' Capacitor Power Relay
8205, # Alpha Reactor Control: Capacitor Power Relay
@@ -131,11 +130,12 @@ CONVERSIONS = {
),
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -4,5 +4,6 @@ Migration 5
Simply deletes damage profiles with a blank name. See GH issue #256
"""
def upgrade(saveddata_engine):
saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ?', ("",))

View File

@@ -4,6 +4,8 @@ Migration 6
Overwrites damage profile 0 to reset bad uniform values (bad values set with bug)
"""
def upgrade(saveddata_engine):
saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ? OR ID LIKE ?', ("Uniform", "1"))
saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)', (1, "Uniform", 25, 25, 25, 25, None))
saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)',
(1, "Uniform", 25, 25, 25, 25, None))

View File

@@ -8,17 +8,16 @@ Migration 7
Pyfa.
"""
CONVERSIONS = {
640: ( # Scorpion
4005, # Scorpion Ishukone Watch
)
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert ships
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?',
(replacement_item, retired_item))

View File

@@ -7,7 +7,6 @@ Migration 8
modules with their new replacements
"""
CONVERSIONS = {
8529: ( # Large F-S9 Regolith Compact Shield Extender
8409, # Large Subordinate Screen Stabilizer I
@@ -71,15 +70,16 @@ CONVERSIONS = {
11321, # 800mm Reinforced Nanofiber Plates I
),
11317: ( # 800mm Rolled Tungsten Compact Plates
11315, # 800mm Reinforced Titanium Plates I
11315, # 800mm Reinforced Titanium Plates I
),
}
def upgrade(saveddata_engine):
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.iteritems():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item))
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -16,8 +16,10 @@ CREATE TABLE boostersTemp (
)
"""
def upgrade(saveddata_engine):
saveddata_engine.execute(tmpTable)
saveddata_engine.execute("INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters")
saveddata_engine.execute(
"INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters")
saveddata_engine.execute("DROP TABLE boosters")
saveddata_engine.execute("ALTER TABLE boostersTemp RENAME TO boosters")

View File

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import mapper, relation
from eos.db import saveddata_meta
from eos.types import Booster
from eos.saveddata.booster import Booster
boosters_table = Table("boosters", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Cargo
from eos.saveddata.cargo import Cargo
cargo_table = Table("cargo", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -23,7 +23,9 @@ from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.db.saveddata.implant import charImplants_table
from eos.effectHandlerHelpers import HandledImplantBoosterList
from eos.types import Character, User, Skill, Implant
from eos.saveddata.implant import Implant
from eos.saveddata.user import User
from eos.saveddata.character import Character, Skill
characters_table = Table("characters", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -33,10 +35,12 @@ characters_table = Table("characters", saveddata_meta,
Column("defaultChar", Integer),
Column("chars", String, nullable=True),
Column("defaultLevel", Integer, nullable=True),
Column("alphaCloneID", Integer, nullable=True),
Column("ownerID", ForeignKey("users.ID"), nullable=True))
mapper(Character, characters_table,
properties={
"_Character__alphaCloneID": characters_table.c.alphaCloneID,
"savedName": characters_table.c.name,
"_Character__owner": relation(
User,

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import CrestChar
from eos.saveddata.crestchar import CrestChar
crest_table = Table("crest", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import DamagePattern
from eos.saveddata.damagePattern import DamagePattern
damagePatterns_table = Table("damagePatterns", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -17,88 +17,223 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import sqlalchemy
import logging
from sqlalchemy.exc import DatabaseError
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class DatabaseCleanup:
class DatabaseCleanup(object):
def __init__(self):
pass
@staticmethod
def OrphanedCharacterSkills(saveddata_engine):
# Finds and fixes database corruption issues.
logger.debug("Start databsae validation and cleanup.")
def ExecuteSQLQuery(saveddata_engine, query):
try:
results = saveddata_engine.execute(query)
return results
except DatabaseError:
pyfalog.error("Failed to connect to database or error executing query:\n{0}", query)
return None
@staticmethod
def OrphanedCharacterSkills(saveddata_engine):
# Find orphaned character skills.
# This solves an issue where the character doesn't exist, but skills for that character do.
# See issue #917
try:
logger.debug("Running database cleanup for character skills.")
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills "
"WHERE characterID NOT IN (SELECT ID from characters)")
row = results.first()
pyfalog.debug("Running database cleanup for character skills.")
query = "SELECT COUNT(*) AS num FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if row and row['num']:
delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)")
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
if results is None:
return
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")
row = results.first()
if row and row['num']:
query = "DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitDamagePatterns(saveddata_engine):
# Find orphaned damage patterns.
# This solves an issue where the damage pattern doesn't exist, but fits reference the pattern.
# See issue #777
try:
logger.debug("Running database cleanup for orphaned damage patterns attached to fits.")
pyfalog.debug("Running database cleanup for orphaned damage patterns attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL")
row = results.first()
if results is None:
return
if row and row['num']:
# Get Uniform damage pattern ID
query = saveddata_engine.execute("SELECT ID FROM damagePatterns WHERE name = 'Uniform'")
rows = query.fetchall()
row = results.first()
if len(rows) == 0:
logger.error("Missing uniform damage pattern.")
elif len(rows) > 1:
logger.error("More than one uniform damage pattern found.")
else:
uniform_damage_pattern_id = rows[0]['ID']
update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? "
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL",
uniform_damage_pattern_id)
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")
if row and row['num']:
# Get Uniform damage pattern ID
uniform_query = "SELECT ID FROM damagePatterns WHERE name = 'Uniform'"
uniform_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, uniform_query)
if uniform_results is None:
return
rows = uniform_results.fetchall()
if len(rows) == 0:
pyfalog.error("Missing uniform damage pattern.")
elif len(rows) > 1:
pyfalog.error("More than one uniform damage pattern found.")
else:
uniform_damage_pattern_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'damagePatternID' = {} " \
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id)
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def OrphanedFitCharacterIDs(saveddata_engine):
# Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern.
try:
logger.debug("Running database cleanup for orphaned characters attached to fits.")
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL")
pyfalog.debug("Running database cleanup for orphaned characters attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num']:
# Get All 5 character ID
all5_query = "SELECT ID FROM characters WHERE name = 'All 5'"
all5_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, all5_query)
if all5_results is None:
return
rows = all5_results.fetchall()
if len(rows) == 0:
pyfalog.error("Missing 'All 5' character.")
elif len(rows) > 1:
pyfalog.error("More than one 'All 5' character found.")
else:
all5_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'characterID' = " + str(all5_id) + \
" WHERE characterID not in (select ID from characters) OR characterID IS NULL"
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def NullDamagePatternNames(saveddata_engine):
# Find damage patterns that are missing the name.
# This solves an issue where the damage pattern ends up with a name that is null.
# See issue #949
pyfalog.debug("Running database cleanup for missing damage pattern names.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num']:
query = "DELETE FROM damagePatterns WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullTargetResistNames(saveddata_engine):
# Find target resists that are missing the name.
# This solves an issue where the target resist ends up with a name that is null.
# See issue #949
pyfalog.debug("Running database cleanup for missing target resist names.")
query = "SELECT COUNT(*) AS num FROM targetResists WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num']:
query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitIDItemID(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
for table in ['drones', 'cargo', 'fighters']:
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row['num']:
# Get All 5 character ID
query = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'")
rows = query.fetchall()
if row and row['num']:
query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
if len(rows) == 0:
logger.error("Missing 'All 5' character.")
elif len(rows) > 1:
logger.error("More than one 'All 5' character found.")
else:
all5_id = rows[0]['ID']
update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? "
"WHERE characterID not in (select ID from characters) OR characterID IS NULL",
all5_id)
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")
for table in ['modules']:
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num']:
query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullDamageTargetPatternValues(saveddata_engine):
# Find patterns that have null values
# See issue #954
for profileType in ['damagePatterns', 'targetResists']:
for damageType in ['em', 'thermal', 'kinetic', 'explosive']:
pyfalog.debug("Running database cleanup for null {0} values. ({1})", profileType, damageType)
query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType,
damageType)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num']:
query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType,
damageType)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def DuplicateSelectedAmmoName(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
pyfalog.debug("Running database cleanup for duplicated selected ammo profiles.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
if results is None:
return
row = results.first()
if row and row['num'] > 1:
query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Drone
from eos.saveddata.drone import Drone
drones_table = Table("drones", saveddata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -18,11 +18,12 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import *
from sqlalchemy.orm import mapper, relation
from eos.db import saveddata_meta
from eos.types import Fighter, Fit
from eos.types import FighterAbility
from eos.saveddata.fighterAbility import FighterAbility
from eos.saveddata.fighter import Fighter
from eos.saveddata.fit import Fit
fighters_table = Table("fighters", saveddata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -17,21 +17,32 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import *
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import *
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.sql import and_
from sqlalchemy.orm import relation, reconstructor, mapper, relationship
from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean
from eos.db import saveddata_meta
from eos.db import saveddata_session
from eos.db.saveddata.cargo import cargo_table
from eos.db.saveddata.drone import drones_table
from eos.db.saveddata.fighter import fighters_table
from eos.db.saveddata.implant import fitImplants_table
from eos.db.saveddata.module import modules_table
from eos.effectHandlerHelpers import *
from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \
TargetResists, ImplantLocation
from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \
HandledDroneCargoList, HandledProjectedDroneList
from eos.saveddata.implant import Implant
from eos.saveddata.character import Character
from eos.saveddata.user import User
from eos.saveddata.fighter import Fighter
from eos.saveddata.fit import Fit as es_Fit, ImplantLocation
from eos.saveddata.drone import Drone
from eos.saveddata.booster import Booster
from eos.saveddata.module import Module
from eos.saveddata.cargo import Cargo
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.targetResists import TargetResists
fits_table = Table("fits", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -45,7 +56,7 @@ fits_table = Table("fits", saveddata_meta,
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
Column("modeID", Integer, nullable=True),
Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT),
Column("notes", String, nullable = True),
Column("notes", String, nullable=True),
)
projectedFits_table = Table("projectedFits", saveddata_meta,
@@ -61,6 +72,7 @@ commandFits_table = Table("commandFits", saveddata_meta,
Column("active", Boolean, nullable=False, default=1)
)
class ProjectedFit(object):
def __init__(self, sourceID, source_fit, amount=1, active=True):
self.sourceID = sourceID
@@ -72,9 +84,9 @@ class ProjectedFit(object):
def init(self):
if self.source_fit.isInvalid:
# Very rare for this to happen, but be prepared for it
eos.db.saveddata_session.delete(self.source_fit)
eos.db.saveddata_session.flush()
eos.db.saveddata_session.refresh(self.victim_fit)
saveddata_session.delete(self.source_fit)
saveddata_session.flush()
saveddata_session.refresh(self.victim_fit)
# We have a series of setters and getters here just in case someone
# downgrades and screws up the table with NULL values
@@ -91,6 +103,7 @@ class ProjectedFit(object):
self.sourceID, self.victimID, self.amount, self.active, hex(id(self))
)
class CommandFit(object):
def __init__(self, boosterID, booster_fit, active=True):
self.boosterID = boosterID
@@ -101,125 +114,126 @@ class CommandFit(object):
def init(self):
if self.booster_fit.isInvalid:
# Very rare for this to happen, but be prepared for it
eos.db.saveddata_session.delete(self.booster_fit)
eos.db.saveddata_session.flush()
eos.db.saveddata_session.refresh(self.boosted_fit)
saveddata_session.delete(self.booster_fit)
saveddata_session.flush()
saveddata_session.refresh(self.boosted_fit)
def __repr__(self):
return "CommandFit(boosterID={}, boostedID={}, active={}) at {}".format(
self.boosterID, self.boostedID, self.active, hex(id(self))
)
Fit._Fit__projectedFits = association_proxy(
es_Fit._Fit__projectedFits = association_proxy(
"victimOf", # look at the victimOf association...
"source_fit", # .. and return the source fits
creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit)
)
Fit._Fit__commandFits = association_proxy(
es_Fit._Fit__commandFits = association_proxy(
"boostedOf", # look at the boostedOf association...
"booster_fit", # .. and return the booster fit
creator=lambda boosterID, booster_fit: CommandFit(boosterID, booster_fit)
)
mapper(Fit, fits_table,
properties={
"_Fit__modules": relation(
Module,
collection_class=HandledModuleList,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False),
order_by=modules_table.c.position,
cascade='all, delete, delete-orphan'),
"_Fit__projectedModules": relation(
Module,
collection_class=HandledProjectedModList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)),
"owner": relation(
User,
backref="fits"),
"itemID": fits_table.c.shipID,
"shipID": fits_table.c.shipID,
"_Fit__boosters": relation(
Booster,
collection_class=HandledImplantBoosterList,
cascade='all, delete, delete-orphan',
single_parent=True),
"_Fit__drones": relation(
Drone,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
"_Fit__fighters": relation(
Fighter,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)),
"_Fit__cargo": relation(
Cargo,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)),
"_Fit__projectedDrones": relation(
Drone,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
"_Fit__projectedFighters": relation(
Fighter,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)),
"_Fit__implants": relation(
Implant,
collection_class=HandledImplantBoosterList,
cascade='all, delete, delete-orphan',
backref='fit',
single_parent=True,
primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID,
secondaryjoin=fitImplants_table.c.implantID == Implant.ID,
secondary=fitImplants_table),
"_Fit__character": relation(
Character,
backref="fits"),
"_Fit__damagePattern": relation(DamagePattern),
"_Fit__targetResists": relation(TargetResists),
"projectedOnto": relationship(
ProjectedFit,
primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID,
backref='source_fit',
collection_class=attribute_mapped_collection('victimID'),
cascade='all, delete, delete-orphan'),
"victimOf": relationship(
ProjectedFit,
primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID,
backref='victim_fit',
collection_class=attribute_mapped_collection('sourceID'),
cascade='all, delete, delete-orphan'),
"boostedOnto": relationship(
CommandFit,
primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID,
backref='booster_fit',
collection_class=attribute_mapped_collection('boostedID'),
cascade='all, delete, delete-orphan'),
"boostedOf": relationship(
CommandFit,
primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID,
backref='boosted_fit',
collection_class=attribute_mapped_collection('boosterID'),
cascade='all, delete, delete-orphan'),
}
)
mapper(es_Fit, fits_table,
properties={
"_Fit__modules": relation(
Module,
collection_class=HandledModuleList,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa
order_by=modules_table.c.position,
cascade='all, delete, delete-orphan'),
"_Fit__projectedModules": relation(
Module,
collection_class=HandledProjectedModList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa
"owner": relation(
User,
backref="fits"),
"itemID": fits_table.c.shipID,
"shipID": fits_table.c.shipID,
"_Fit__boosters": relation(
Booster,
collection_class=HandledImplantBoosterList,
cascade='all, delete, delete-orphan',
single_parent=True),
"_Fit__drones": relation(
Drone,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa
"_Fit__fighters": relation(
Fighter,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa
"_Fit__cargo": relation(
Cargo,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)),
"_Fit__projectedDrones": relation(
Drone,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa
"_Fit__projectedFighters": relation(
Fighter,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
"_Fit__implants": relation(
Implant,
collection_class=HandledImplantBoosterList,
cascade='all, delete, delete-orphan',
backref='fit',
single_parent=True,
primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID,
secondaryjoin=fitImplants_table.c.implantID == Implant.ID,
secondary=fitImplants_table),
"_Fit__character": relation(
Character,
backref="fits"),
"_Fit__damagePattern": relation(DamagePattern),
"_Fit__targetResists": relation(TargetResists),
"projectedOnto": relationship(
ProjectedFit,
primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID,
backref='source_fit',
collection_class=attribute_mapped_collection('victimID'),
cascade='all, delete, delete-orphan'),
"victimOf": relationship(
ProjectedFit,
primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID,
backref='victim_fit',
collection_class=attribute_mapped_collection('sourceID'),
cascade='all, delete, delete-orphan'),
"boostedOnto": relationship(
CommandFit,
primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID,
backref='booster_fit',
collection_class=attribute_mapped_collection('boostedID'),
cascade='all, delete, delete-orphan'),
"boostedOf": relationship(
CommandFit,
primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID,
backref='boosted_fit',
collection_class=attribute_mapped_collection('boosterID'),
cascade='all, delete, delete-orphan'),
}
)
mapper(ProjectedFit, projectedFits_table,
properties={
"_ProjectedFit__amount": projectedFits_table.c.amount,
}
)
properties={
"_ProjectedFit__amount": projectedFits_table.c.amount,
}
)
mapper(CommandFit, commandFits_table)
mapper(CommandFit, commandFits_table)

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Implant
from eos.saveddata.implant import Implant
implants_table = Table("implants", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -23,7 +23,8 @@ from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.db.saveddata.implant import implantsSetMap_table
from eos.effectHandlerHelpers import HandledImplantBoosterList
from eos.types import Implant, ImplantSet
from eos.saveddata.implant import Implant
from eos.saveddata.implantSet import ImplantSet
implant_set_table = Table("implantSets", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -18,14 +18,15 @@
# ===============================================================================
import eos.db
import eos.types
from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern
from eos.saveddata.targetResists import TargetResists as es_TargetResists
class ImportError(Exception):
pass
class DefaultDatabaseValues():
class DefaultDatabaseValues(object):
def __init__(self):
pass
@@ -118,7 +119,7 @@ class DefaultDatabaseValues():
name, em, therm, kin, exp = damageProfileRow
damageProfile = eos.db.getDamagePattern(name)
if damageProfile is None:
damageProfile = eos.types.DamagePattern(em, therm, kin, exp)
damageProfile = es_DamagePattern(em, therm, kin, exp)
damageProfile.name = name
eos.db.save(damageProfile)
@@ -180,7 +181,7 @@ class DefaultDatabaseValues():
name, em, therm, kin, exp = targetResistProfileRow
resistsProfile = eos.db.eos.db.getTargetResists(name)
if resistsProfile is None:
resistsProfile = eos.types.TargetResists(em, therm, kin, exp)
resistsProfile = es_TargetResists(em, therm, kin, exp)
resistsProfile.name = name
eos.db.save(resistsProfile)
@@ -192,6 +193,6 @@ class DefaultDatabaseValues():
name, em, therm, kin, exp = damageProfileRow
damageProfile = eos.db.getDamagePattern(name)
if damageProfile is None:
damageProfile = eos.types.DamagePattern(em, therm, kin, exp)
damageProfile = es_DamagePattern(em, therm, kin, exp)
damageProfile.name = name
eos.db.save(damageProfile)

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import MiscData
from eos.saveddata.miscData import MiscData
miscdata_table = Table("miscdata", saveddata_meta,
Column("fieldName", String, primary_key=True),

View File

@@ -21,7 +21,8 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, CheckConstraint, Bool
from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.types import Module, Fit
from eos.saveddata.module import Module
from eos.saveddata.fit import Fit
modules_table = Table("modules", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Override
from eos.saveddata.override import Override
overrides_table = Table("overrides", saveddata_meta,
Column("itemID", Integer, primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Float, Integer
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Price
from eos.saveddata.price import Price
prices_table = Table("prices", saveddata_meta,
Column("typeID", Integer, primary_key=True),

View File

@@ -1,4 +1,4 @@
#===============================================================================
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
@@ -15,26 +15,40 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
#===============================================================================
# ===============================================================================
from eos.db.util import processEager, processWhere
from eos.db import saveddata_session, sd_lock
from eos.types import *
from eos.db.saveddata.fit import projectedFits_table
from sqlalchemy.sql import and_
from eos.db import saveddata_session, sd_lock
from eos.db.saveddata.fit import projectedFits_table
from eos.db.util import processEager, processWhere
from eos.saveddata.price import Price
from eos.saveddata.user import User
from eos.saveddata.crestchar import CrestChar
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.targetResists import TargetResists
from eos.saveddata.character import Character
from eos.saveddata.implantSet import ImplantSet
from eos.saveddata.fit import Fit
from eos.saveddata.miscData import MiscData
from eos.saveddata.override import Override
import eos.config
configVal = getattr(eos.config, "saveddataCache", None)
if configVal is True:
import weakref
itemCache = {}
queryCache = {}
def cachedQuery(type, amount, *keywords):
itemCache[type] = localItemCache = weakref.WeakValueDictionary()
queryCache[type] = typeQueryCache = {}
def deco(function):
localQueryCache = typeQueryCache[function] = {}
def setCache(cacheKey, args, kwargs):
items = function(*args, **kwargs)
IDs = set()
@@ -43,7 +57,7 @@ if configVal is True:
for item in stuff:
ID = getattr(item, "ID", None)
if ID is None:
#Some uncachable data, don't cache this query
# Some uncachable data, don't cache this query
del localQueryCache[cacheKey]
break
localItemCache[ID] = item
@@ -54,6 +68,7 @@ if configVal is True:
def checkAndReturn(*args, **kwargs):
useCache = kwargs.pop("useCache", True)
cacheKey = []
items = None
cacheKey.extend(args)
for keyword in keywords:
cacheKey.append(kwargs.get(keyword))
@@ -69,7 +84,7 @@ if configVal is True:
for ID in IDs:
data = localItemCache.get(ID)
if data is None:
#Fuck, some of our stuff isn't cached it seems.
# Fuck, some of our stuff isn't cached it seems.
items = setCache(cacheKey, args, kwargs)
break
items.append(data)
@@ -81,11 +96,13 @@ if configVal is True:
break
return items
return checkAndReturn
return deco
def removeCachedEntry(type, ID):
if not type in queryCache:
if type not in queryCache:
return
functionCache = queryCache[type]
for _, localCache in functionCache.iteritems():
@@ -110,11 +127,13 @@ else:
return function(*args, **kwargs)
return checkAndReturn
return deco
def removeCachedEntry(*args, **kwargs):
return
def sqlizeString(line):
# Escape backslashes first, as they will be as escape symbol in queries
# Then escape percent and underscore signs
@@ -122,6 +141,7 @@ def sqlizeString(line):
line = line.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_").replace("*", "%")
return line
@cachedQuery(User, 1, "lookfor")
def getUser(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -140,6 +160,7 @@ def getUser(lookfor, eager=None):
raise TypeError("Need integer or string as argument")
return user
@cachedQuery(Character, 1, "lookfor")
def getCharacter(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -153,17 +174,20 @@ def getCharacter(lookfor, eager=None):
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
character = saveddata_session.query(Character).options(*eager).filter(Character.savedName == lookfor).first()
character = saveddata_session.query(Character).options(*eager).filter(
Character.savedName == lookfor).first()
else:
raise TypeError("Need integer or string as argument")
return character
def getCharacterList(eager=None):
eager = processEager(eager)
with sd_lock:
characters = saveddata_session.query(Character).options(*eager).all()
return characters
def getCharactersForUser(lookfor, eager=None):
if isinstance(lookfor, int):
eager = processEager(eager)
@@ -173,6 +197,7 @@ def getCharactersForUser(lookfor, eager=None):
raise TypeError("Need integer as argument")
return characters
@cachedQuery(Fit, 1, "lookfor")
def getFit(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -193,6 +218,7 @@ def getFit(lookfor, eager=None):
return fit
def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
"""
Get all the fits using a certain ship.
@@ -214,6 +240,7 @@ def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
return fits
def getBoosterFits(ownerID=None, where=None, eager=None):
"""
Get all the fits that are flagged as a boosting ship
@@ -233,31 +260,41 @@ def getBoosterFits(ownerID=None, where=None, eager=None):
return fits
def countAllFits():
with sd_lock:
count = saveddata_session.query(Fit).count()
return count
def countFitsWithShip(shipID, ownerID=None, where=None, eager=None):
def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None):
"""
Get all the fits using a certain ship.
If no user is passed, do this for all users.
"""
if isinstance(shipID, int):
if ownerID is not None and not isinstance(ownerID, int):
raise TypeError("OwnerID must be integer")
filter = Fit.shipID == shipID
if ownerID is not None:
filter = and_(filter, Fit.ownerID == ownerID)
if ownerID is not None and not isinstance(ownerID, int):
raise TypeError("OwnerID must be integer")
filter = processWhere(filter, where)
eager = processEager(eager)
with sd_lock:
count = saveddata_session.query(Fit).options(*eager).filter(filter).count()
if isinstance(lookfor, int):
filter = Fit.shipID == lookfor
elif isinstance(lookfor, list):
if len(lookfor) == 0:
return 0
filter = Fit.shipID.in_(lookfor)
else:
raise TypeError("ShipID must be integer")
raise TypeError("You must supply either an integer or ShipID must be integer")
if ownerID is not None:
filter = and_(filter, Fit.ownerID == ownerID)
filter = processWhere(filter, where)
eager = processEager(eager)
with sd_lock:
count = saveddata_session.query(Fit).options(*eager).filter(filter).count()
return count
def getFitList(eager=None):
eager = processEager(eager)
with sd_lock:
@@ -265,6 +302,7 @@ def getFitList(eager=None):
return fits
@cachedQuery(Price, 1, "typeID")
def getPrice(typeID):
if isinstance(typeID, int):
@@ -274,12 +312,14 @@ def getPrice(typeID):
raise TypeError("Need integer as argument")
return price
def clearPrices():
with sd_lock:
deleted_rows = saveddata_session.query(Price).delete()
commit()
return deleted_rows
def getMiscData(field):
if isinstance(field, basestring):
with sd_lock:
@@ -288,24 +328,28 @@ def getMiscData(field):
raise TypeError("Need string as argument")
return data
def getDamagePatternList(eager=None):
eager = processEager(eager)
with sd_lock:
patterns = saveddata_session.query(DamagePattern).options(*eager).all()
return patterns
def getTargetResistsList(eager=None):
eager = processEager(eager)
with sd_lock:
patterns = saveddata_session.query(TargetResists).options(*eager).all()
return patterns
def getImplantSetList(eager=None):
eager = processEager(eager)
with sd_lock:
sets = saveddata_session.query(ImplantSet).options(*eager).all()
return sets
@cachedQuery(DamagePattern, 1, "lookfor")
def getDamagePattern(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -315,15 +359,18 @@ def getDamagePattern(lookfor, eager=None):
else:
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(DamagePattern.ID == lookfor).first()
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
DamagePattern.ID == lookfor).first()
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(DamagePattern.name == lookfor).first()
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
DamagePattern.name == lookfor).first()
else:
raise TypeError("Need integer or string as argument")
return pattern
@cachedQuery(TargetResists, 1, "lookfor")
def getTargetResists(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -333,15 +380,18 @@ def getTargetResists(lookfor, eager=None):
else:
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(TargetResists).options(*eager).filter(TargetResists.ID == lookfor).first()
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
TargetResists.ID == lookfor).first()
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(TargetResists).options(*eager).filter(TargetResists.name == lookfor).first()
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
TargetResists.name == lookfor).first()
else:
raise TypeError("Need integer or string as argument")
return pattern
@cachedQuery(ImplantSet, 1, "lookfor")
def getImplantSet(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -351,7 +401,8 @@ def getImplantSet(lookfor, eager=None):
else:
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.ID == lookfor).first()
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(
TargetResists.ID == lookfor).first()
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
@@ -360,13 +411,14 @@ def getImplantSet(lookfor, eager=None):
raise TypeError("Improper argument")
return pattern
def searchFits(nameLike, where=None, eager=None):
if not isinstance(nameLike, basestring):
raise TypeError("Need string as argument")
# Prepare our string for request
nameLike = u"%{0}%".format(sqlizeString(nameLike))
#Add any extra components to the search to our where clause
# Add any extra components to the search to our where clause
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
eager = processEager(eager)
with sd_lock:
@@ -374,6 +426,7 @@ def searchFits(nameLike, where=None, eager=None):
return fits
def getProjectedFits(fitID):
if isinstance(fitID, int):
with sd_lock:
@@ -383,12 +436,14 @@ def getProjectedFits(fitID):
else:
raise TypeError("Need integer as argument")
def getCrestCharacters(eager=None):
eager = processEager(eager)
with sd_lock:
characters = saveddata_session.query(CrestChar).options(*eager).all()
return characters
@cachedQuery(CrestChar, 1, "lookfor")
def getCrestCharacter(lookfor, eager=None):
if isinstance(lookfor, int):
@@ -407,21 +462,25 @@ def getCrestCharacter(lookfor, eager=None):
raise TypeError("Need integer or string as argument")
return character
def getOverrides(itemID, eager=None):
if isinstance(itemID, int):
return saveddata_session.query(Override).filter(Override.itemID == itemID).all()
else:
raise TypeError("Need integer as argument")
def clearOverrides():
with sd_lock:
deleted_rows = saveddata_session.query(Override).delete()
commit()
return deleted_rows
def getAllOverrides(eager=None):
return saveddata_session.query(Override).all()
def removeInvalid(fits):
invalids = [f for f in fits if f.isInvalid]
@@ -432,14 +491,17 @@ def removeInvalid(fits):
return fits
def add(stuff):
with sd_lock:
saveddata_session.add(stuff)
def save(stuff):
add(stuff)
commit()
def remove(stuff):
removeCachedEntry(type(stuff), stuff.ID)
with sd_lock:

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Skill
from eos.saveddata.character import Skill
skills_table = Table("characterSkills", saveddata_meta,
Column("characterID", ForeignKey("characters.ID"), primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float, ForeignKey, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import TargetResists
from eos.saveddata.targetResists import TargetResists
targetResists_table = Table("targetResists", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import User
from eos.saveddata.user import User
users_table = Table("users", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -17,13 +17,9 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
# from sqlalchemy.orm.attributes import flag_modified
import logging
from logbook import Logger
import eos.db
import eos.types
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class HandledList(list):
@@ -140,10 +136,6 @@ class HandledModuleList(HandledList):
self.remove(mod)
return
# fix for #529, where a module may be in incorrect state after CCP changes mechanics of module
if not mod.isValidState(mod.state):
mod.state = eos.types.State.ONLINE
def insert(self, index, mod):
mod.position = index
i = index
@@ -163,7 +155,7 @@ class HandledModuleList(HandledList):
def toDummy(self, index):
mod = self[index]
if not mod.isEmpty:
dummy = eos.types.Module.buildEmpty(mod.slot)
dummy = mod.buildEmpty(mod.slot)
dummy.position = index
self[index] = dummy
@@ -172,10 +164,11 @@ class HandledModuleList(HandledList):
self[index] = mod
def freeSlot(self, slot):
for i in range(len(self) - 1, -1, -1):
for i in range(len(self)):
mod = self[i]
if mod.getModifiedItemAttr("subSystemSlot") == slot:
del self[i]
self.toDummy(i)
break
class HandledDroneCargoList(HandledList):
@@ -205,7 +198,7 @@ class HandledImplantBoosterList(HandledList):
# if needed, remove booster that was occupying slot
oldObj = next((m for m in self if m.slot == thing.slot), None)
if oldObj:
logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name)
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name)
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
self.remove(oldObj)
@@ -229,7 +222,7 @@ class HandledProjectedModList(HandledList):
oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
if oldEffect:
logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name)
pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
self.remove(oldEffect)
HandledList.append(self, proj)

View File

@@ -2,19 +2,19 @@
#
# Used by:
# Module: Reactive Armor Hardener
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
runTime = "late"
type = "active"
def handler(fit, module, context):
damagePattern = fit.damagePattern
# 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'))
# Populate a tuple with the damage profile modified by current armor resists.
baseDamageTaken = (
@@ -23,35 +23,35 @@ 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])
# pyfalog.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
resistanceShiftAmount = module.getModifiedItemAttr(
'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction
RAHResistance = [
module.getModifiedItemAttr('armorEmDamageResonance'),
module.getModifiedItemAttr('armorThermalDamageResonance'),
module.getModifiedItemAttr('armorKineticDamageResonance'),
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 = -20
for num in range(50):
#logger.debug("Starting cycle %d.", num)
# pyfalog.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]),
(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
# Since the resistances not taking damage will end up going to the type taking damage we just do the whole thing at once.
@@ -72,41 +72,47 @@ def handler(fit, module, context):
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])
# pyfalog.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-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:
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)
# pyfalog.debug("Loop found: %d-%d", loopStart, 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 equilibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# Average the profiles in the RAH loop, or the last 20 if it didn't find a loop.
pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}",
baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# 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]
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])
for i, attr in enumerate(('armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')):
# pyfalog.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.increaseItemAttr(attr, average[i] - module.getModifiedItemAttr(attr))
fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul")

View File

@@ -1,7 +1,7 @@
# ammoInfluenceCapNeed
#
# Used by:
# Items from category: Charge (465 of 899)
# Items from category: Charge (465 of 912)
# Charges from group: Frequency Crystal (185 of 185)
# Charges from group: Hybrid Charge (209 of 209)
type = "passive"

View File

@@ -1,7 +1,7 @@
# ammoInfluenceRange
#
# Used by:
# Items from category: Charge (571 of 899)
# Items from category: Charge (571 of 912)
type = "passive"

View File

@@ -1,7 +1,7 @@
# ammoSpeedMultiplier
#
# Used by:
# Charges from group: Festival Charges (9 of 9)
# Charges from group: Festival Charges (22 of 22)
# Charges from group: Interdiction Probe (2 of 2)
# Charges from group: Survey Probe (3 of 3)
type = "passive"

View File

@@ -8,7 +8,7 @@ type = "passive"
def handler(fit, implant, context):
fit.appliedImplants.filteredItemMultiply(
lambda
implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes,
lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and
"implantSetAngel" in implant.itemModifiedAttributes,
"signatureRadiusBonus",
implant.getModifiedItemAttr("implantSetAngel"))

View File

@@ -3,6 +3,9 @@
# Used by:
# Skill: Armored Command
type = "passive"
def handler(fit, src, context):
lvl = src.level
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
src.getModifiedItemAttr("durationBonus") * lvl)

View File

@@ -5,9 +5,16 @@
# Implant: Federation Navy Command Mindlink
# Implant: Imperial Navy Command Mindlink
type = "passive"
def handler(fit, src, context):
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier",
src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier",
src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier",
src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier",
src.getModifiedItemAttr("mindlinkBonus"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
src.getModifiedItemAttr("mindlinkBonus"))

View File

@@ -3,9 +3,15 @@
# Used by:
# Skill: Armored Command Specialist
type = "passive"
def handler(fit, src, context):
lvl = src.level
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier",
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier",
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier",
src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier",
src.getModifiedItemAttr("commandStrengthBonus") * lvl)

View File

@@ -3,7 +3,6 @@
# Used by:
# Modules from group: Armor Coating (202 of 202)
# Modules from group: Armor Plating Energized (187 of 187)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"

View File

@@ -1,7 +1,7 @@
# armorRepairAmountBonusSubcap
#
# Used by:
# Implants named like: Grade Asklepian (15 of 16)
# Implants named like: grade Asklepian (15 of 18)
type = "passive"

View File

@@ -7,5 +7,7 @@ type = "passive"
def handler(fit, ship, context):
for sensorType in ("Gravimetric", "Ladar", "Magnetometric", "Radar"):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"), "scan{0}StrengthBonus".format(sensorType),
ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True, skill="Caldari Battleship")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"),
"scan{0}StrengthBonus".format(sensorType),
ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True,
skill="Caldari Battleship")

View File

@@ -4,7 +4,8 @@
# Items from market group: Ammunition & Charges > Command Burst Charges (15 of 15)
type = "active"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x))
module.multiplyItemAttr("warfareBuff{}Value".format(x), value)
module.multiplyItemAttr("warfareBuff{}Value".format(x), value)

View File

@@ -5,6 +5,8 @@
# Skill: Leadership
# Skill: Wing Command
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"),
"maxRange",

View File

@@ -11,5 +11,8 @@
# Ship: Orca
# Ship: Rorqual
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", src.getModifiedItemAttr("roleBonusCommandBurstAoERange"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange",
src.getModifiedItemAttr("roleBonusCommandBurstAoERange"))

View File

@@ -3,6 +3,8 @@
# Used by:
# Skill: Command Burst Specialist
type = "passive"
def handler(fit, src, context):
lvl = src.level
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"),

View File

@@ -3,6 +3,10 @@
# Used by:
# Modules named like: Command Processor I (4 of 4)
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive",
src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline",
src.getModifiedItemAttr("maxGangModules"))

View File

@@ -6,6 +6,9 @@
# Ship: Rorqual
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive",
src.getModifiedItemAttr("maxGangModules"))
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline",
src.getModifiedItemAttr("maxGangModules"))

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Data Miners (15 of 16)
# Module: QA Cross Protocol Analyzer
type = "active"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Automated Targeting System (6 of 6)
# Module: QA Damage Module
type = "passive"

View File

@@ -4,9 +4,16 @@
# Ship: Magus
# Ship: Pontifex
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")

View File

@@ -4,9 +4,16 @@
# Ship: Pontifex
# Ship: Stork
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")

View File

@@ -3,4 +3,7 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"),
"commandBonusHidden",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"),
skill="Command Destroyers")

View File

@@ -4,9 +4,16 @@
# Ship: Bifrost
# Ship: Stork
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")

View File

@@ -4,9 +4,16 @@
# Ship: Bifrost
# Ship: Magus
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")

View File

@@ -3,9 +3,16 @@
# Used by:
# Ships from group: Command Ship (4 of 8)
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")

View File

@@ -3,9 +3,16 @@
# Used by:
# Ships from group: Command Ship (4 of 8)
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")

View File

@@ -1,5 +1,7 @@
# Not used by any item
type = "passive"
def handler(fit, module, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"),
"commandBonusHidden", module.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")

View File

@@ -3,9 +3,16 @@
# Used by:
# Ships from group: Command Ship (4 of 8)
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")

View File

@@ -3,9 +3,16 @@
# Used by:
# Ships from group: Command Ship (4 of 8)
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration",
src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships")

View File

@@ -3,7 +3,9 @@
# Used by:
# Ship: Caedes
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Energy Nosferatu", "Energy Neutralizer"),
"falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"), stackingPenalties=True, skill="Covert Ops")
"falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"),
stackingPenalties=True, skill="Covert Ops")

Some files were not shown because too many files have changed in this diff Show More