Merge branch 'test-3'
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -118,4 +118,7 @@ ENV/
|
||||
.idea
|
||||
eos.iml
|
||||
gitversion
|
||||
.version
|
||||
/.version
|
||||
*.swp
|
||||
|
||||
|
||||
14
.mailmap
Normal file
14
.mailmap
Normal file
@@ -0,0 +1,14 @@
|
||||
cncfanatics <diego.duclos@gmail.com> cncfanatics <cncfanatics@titanium.(none)>
|
||||
blitzmann <holmes.ryan.90@gmail.com>
|
||||
blitzmann <holmes.ryan.90@gmail.com> blitzmann <ryan.xgamer99@gmail.com>
|
||||
blitzmann <holmes.ryan.90@gmail.com>
|
||||
blitzmann <holmes.ryan.90@gmail.com> blitzman <ryan.xgamer99@gmail.com>
|
||||
blitzmann <holmes.ryan.90@gmail.com> Ryan Holmes <ryan.holmes.90@gmail.com>
|
||||
blitzmann <holmes.ryan.90@gmail.com>
|
||||
Corollax <corollax@gmail.com> Corollax <corollax@corollax-laptop.(none)>
|
||||
Corollax <corollax@gmail.com> Corollax <corollax@corollax-N76VM.(none)>
|
||||
Mr. Nukealizer <mr.nukealizer@gmail.com> Mr. Nukealizer <MrNukealizer@users.noreply.github.com>
|
||||
DarkPhoenix <phoenix@mail.ru>
|
||||
Sakari Orisi <sakari@evefit.org>
|
||||
Will Wykeham <will@wykeham.net> Will Wykeham <will.wykeham@paconsulting.com>
|
||||
OISumeko <camerongrout@gmail.com> OISumeko <cameron@sporadic.co.nz>
|
||||
@@ -36,13 +36,8 @@ The following is a list of pyfa packages available for certain distributions. Pl
|
||||
### Dependencies
|
||||
If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required:
|
||||
|
||||
* Python 2.7
|
||||
* `wxPython` 2.8/3.0
|
||||
* `sqlalchemy` >= 1.0.5
|
||||
* `dateutil`
|
||||
* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`)
|
||||
* `requests`
|
||||
* `logbook` >= 1.0.0
|
||||
* Python 3.6
|
||||
* Requirements as listed in `requirements.txt`
|
||||
|
||||
## Bug Reporting
|
||||
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting).
|
||||
|
||||
@@ -28,7 +28,7 @@ def DBInMemory_test():
|
||||
gamedataCache = True
|
||||
saveddataCache = True
|
||||
gamedata_version = ""
|
||||
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(unicode(__file__))), "..", "eve.db"))
|
||||
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(str(__file__))), "..", "eve.db"))
|
||||
saveddata_connectionstring = 'sqlite:///:memory:'
|
||||
|
||||
class ReadOnlyException(Exception):
|
||||
@@ -100,8 +100,8 @@ def DBInMemory():
|
||||
import eos.db
|
||||
|
||||
# Output debug info to help us troubleshoot Travis
|
||||
print(eos.db.saveddata_engine)
|
||||
print(eos.db.gamedata_engine)
|
||||
print((eos.db.saveddata_engine))
|
||||
print((eos.db.gamedata_engine))
|
||||
|
||||
helper = {
|
||||
'config': eos.config,
|
||||
|
||||
@@ -41,7 +41,7 @@ def CurseFit(DB, Gamedata, Saveddata):
|
||||
mod.state = Saveddata['State'].ONLINE
|
||||
|
||||
# Add 5 neuts
|
||||
for _ in xrange(5):
|
||||
for _ in range(5):
|
||||
fit.modules.append(mod)
|
||||
|
||||
return fit
|
||||
@@ -60,7 +60,7 @@ def HeronFit(DB, Gamedata, Saveddata):
|
||||
mod.state = Saveddata['State'].ONLINE
|
||||
|
||||
# Add 5 neuts
|
||||
for _ in xrange(4):
|
||||
for _ in range(4):
|
||||
fit.modules.append(mod)
|
||||
|
||||
return fit
|
||||
@@ -94,8 +94,8 @@ def GetUnicodePath(root, file=None, codec=None):
|
||||
path = os.path.join(path, file)
|
||||
|
||||
if codec:
|
||||
path = unicode(path, codec)
|
||||
path = str(path, codec)
|
||||
else:
|
||||
path = unicode(path)
|
||||
path = str(path)
|
||||
|
||||
return path
|
||||
|
||||
173
config.py
173
config.py
@@ -1,7 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from logbook import Logger
|
||||
from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \
|
||||
StreamHandler, TimedRotatingFileHandler, WARNING
|
||||
import hashlib
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -19,17 +23,39 @@ debug = False
|
||||
saveInRoot = False
|
||||
|
||||
# Version data
|
||||
version = "1.36.0"
|
||||
tag = "Stable"
|
||||
|
||||
version = "2.0.0b5"
|
||||
tag = "git"
|
||||
expansionName = "YC120.3"
|
||||
expansionVersion = "1.8"
|
||||
evemonMinVersion = "4081"
|
||||
|
||||
minItemSearchLength = 3
|
||||
|
||||
pyfaPath = None
|
||||
savePath = None
|
||||
saveDB = None
|
||||
gameDB = None
|
||||
logPath = None
|
||||
loggingLevel = None
|
||||
logging_setup = None
|
||||
cipher = None
|
||||
clientHash = None
|
||||
|
||||
ESI_AUTH_PROXY = "https://www.pyfa.io" # "http://localhost:5015"
|
||||
ESI_CACHE = 'esi_cache'
|
||||
|
||||
LOGLEVEL_MAP = {
|
||||
"critical": CRITICAL,
|
||||
"error": ERROR,
|
||||
"warning": WARNING,
|
||||
"info": INFO,
|
||||
"debug": DEBUG,
|
||||
}
|
||||
|
||||
|
||||
def getClientSecret():
|
||||
return clientHash
|
||||
|
||||
|
||||
def isFrozen():
|
||||
@@ -45,23 +71,37 @@ def __createDirs(path):
|
||||
|
||||
|
||||
def getPyfaRoot():
|
||||
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0]
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
return sys._MEIPASS
|
||||
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else __file__
|
||||
root = os.path.dirname(os.path.realpath(os.path.abspath(base)))
|
||||
root = unicode(root, sys.getfilesystemencoding())
|
||||
root = root
|
||||
return root
|
||||
|
||||
|
||||
def getVersion():
|
||||
if os.path.isfile(os.path.join(pyfaPath, '.version')):
|
||||
with open(os.path.join(pyfaPath, '.version')) as f:
|
||||
gitVersion = f.readline()
|
||||
return gitVersion
|
||||
# if no version file exists, then user is running from source or not an official build
|
||||
return version + " (git)"
|
||||
|
||||
|
||||
def getDefaultSave():
|
||||
return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding())
|
||||
return os.path.expanduser(os.path.join("~", ".pyfa"))
|
||||
|
||||
|
||||
def defPaths(customSavePath):
|
||||
def defPaths(customSavePath=None):
|
||||
global debug
|
||||
global pyfaPath
|
||||
global savePath
|
||||
global saveDB
|
||||
global gameDB
|
||||
global saveInRoot
|
||||
global logPath
|
||||
global cipher
|
||||
global clientHash
|
||||
|
||||
pyfalog.debug("Configuring Pyfa")
|
||||
|
||||
@@ -86,12 +126,22 @@ def defPaths(customSavePath):
|
||||
|
||||
__createDirs(savePath)
|
||||
|
||||
if isFrozen():
|
||||
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')
|
||||
secret_file = os.path.join(savePath, ".secret")
|
||||
if not os.path.exists(secret_file):
|
||||
with open(secret_file, "wb") as _file:
|
||||
_file.write(Fernet.generate_key())
|
||||
|
||||
with open(secret_file, 'rb') as fp:
|
||||
key = fp.read()
|
||||
clientHash = hashlib.sha3_256(key).hexdigest()
|
||||
cipher = Fernet(key)
|
||||
|
||||
# if isFrozen():
|
||||
# os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem")
|
||||
# os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem")
|
||||
|
||||
# The database where we store all the fits etc
|
||||
saveDB = os.path.join(savePath, "saveddata.db")
|
||||
saveDB = os.path.join(savePath, "saveddata-py3-dev.db")
|
||||
|
||||
# The database where the static EVE data from the datadump is kept.
|
||||
# This is not the standard sqlite datadump but a modified version created by eos
|
||||
@@ -100,6 +150,13 @@ def defPaths(customSavePath):
|
||||
if not gameDB:
|
||||
gameDB = os.path.join(pyfaPath, "eve.db")
|
||||
|
||||
if debug:
|
||||
logFile = "pyfa_debug.log"
|
||||
else:
|
||||
logFile = "pyfa.log"
|
||||
|
||||
logPath = os.path.join(savePath, logFile)
|
||||
|
||||
# DON'T MODIFY ANYTHING BELOW
|
||||
import eos.config
|
||||
|
||||
@@ -109,6 +166,100 @@ def defPaths(customSavePath):
|
||||
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"
|
||||
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
|
||||
|
||||
print(eos.config.saveddata_connectionstring)
|
||||
print(eos.config.gamedata_connectionstring)
|
||||
|
||||
# initialize the settings
|
||||
from service.settings import EOSSettings
|
||||
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever
|
||||
|
||||
|
||||
def defLogging():
|
||||
global debug
|
||||
global logPath
|
||||
global loggingLevel
|
||||
global logging_setup
|
||||
|
||||
try:
|
||||
if debug:
|
||||
logging_setup = NestedSetup([
|
||||
# make sure we never bubble up to the stderr handler
|
||||
# if we run out of setup handling
|
||||
NullHandler(),
|
||||
StreamHandler(
|
||||
sys.stdout,
|
||||
bubble=False,
|
||||
level=loggingLevel
|
||||
),
|
||||
TimedRotatingFileHandler(
|
||||
logPath,
|
||||
level=0,
|
||||
backup_count=3,
|
||||
bubble=True,
|
||||
date_format='%Y-%m-%d',
|
||||
),
|
||||
])
|
||||
else:
|
||||
logging_setup = NestedSetup([
|
||||
# make sure we never bubble up to the stderr handler
|
||||
# if we run out of setup handling
|
||||
NullHandler(),
|
||||
FingersCrossedHandler(
|
||||
TimedRotatingFileHandler(
|
||||
logPath,
|
||||
level=0,
|
||||
backup_count=3,
|
||||
bubble=False,
|
||||
date_format='%Y-%m-%d',
|
||||
),
|
||||
action_level=ERROR,
|
||||
buffer_size=1000,
|
||||
# pull_information=True,
|
||||
# reset=False,
|
||||
)
|
||||
])
|
||||
except:
|
||||
print("Critical error attempting to setup logging. Falling back to console only.")
|
||||
logging_setup = NestedSetup([
|
||||
# make sure we never bubble up to the stderr handler
|
||||
# if we run out of setup handling
|
||||
NullHandler(),
|
||||
StreamHandler(
|
||||
sys.stdout,
|
||||
bubble=False
|
||||
)
|
||||
])
|
||||
|
||||
with logging_setup.threadbound():
|
||||
|
||||
# Output all stdout (print) messages as warnings
|
||||
try:
|
||||
sys.stdout = LoggerWriter(pyfalog.warning)
|
||||
except:
|
||||
pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.")
|
||||
|
||||
# Output all stderr (stacktrace) messages as critical
|
||||
try:
|
||||
sys.stderr = LoggerWriter(pyfalog.critical)
|
||||
except:
|
||||
pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.")
|
||||
|
||||
|
||||
class LoggerWriter(object):
|
||||
def __init__(self, level):
|
||||
# self.level is really like using log.debug(message)
|
||||
# at least in my case
|
||||
self.level = level
|
||||
|
||||
def write(self, message):
|
||||
# if statement reduces the amount of newlines that are
|
||||
# printed to the logger
|
||||
if message.strip() != '':
|
||||
self.level(message.replace("\n", ""))
|
||||
|
||||
def flush(self):
|
||||
# create a flush method so things can be flushed when
|
||||
# the system wants to. Not sure if simply 'printing'
|
||||
# sys.stderr is the correct way to do it, but it seemed
|
||||
# to work properly for me.
|
||||
self.level(sys.stderr)
|
||||
|
||||
70
dist_assets/linux/pyfa-linux.spec
Normal file
70
dist_assets/linux/pyfa-linux.spec
Normal file
@@ -0,0 +1,70 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
import subprocess
|
||||
|
||||
label = subprocess.check_output([
|
||||
"git", "describe", "--tags"]).strip()
|
||||
|
||||
with open('gitversion', 'w+') as f:
|
||||
f.write(label.decode())
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
added_files = [
|
||||
( 'imgs/gui/*.png', 'imgs/gui' ),
|
||||
( 'imgs/gui/*.gif', 'imgs/gui' ),
|
||||
( 'imgs/icons/*.png', 'imgs/icons' ),
|
||||
( 'imgs/renders/*.png', 'imgs/renders' ),
|
||||
( 'dist_assets/win/pyfa.ico', '.' ),
|
||||
( 'dist_assets/cacert.pem', '.' ),
|
||||
( 'eve.db', '.' ),
|
||||
( 'README.md', '.' ),
|
||||
( 'LICENSE', '.' ),
|
||||
( 'gitversion', '.' ),
|
||||
]
|
||||
|
||||
import_these = []
|
||||
|
||||
# Walk directories that do dynamic importing
|
||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||
for file_ in files:
|
||||
if file_.endswith(".py") and not file_.startswith("_"):
|
||||
mod_name = "{}.{}".format(
|
||||
root.replace("/", "."),
|
||||
file_.split(".py")[0],
|
||||
)
|
||||
import_these.append(mod_name)
|
||||
|
||||
|
||||
a = Analysis(['pyfa.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=import_these,
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
exclude_binaries=True,
|
||||
name='pyfa',
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='pyfa')
|
||||
74
dist_assets/mac/pyfa.spec
Normal file
74
dist_assets/mac/pyfa.spec
Normal file
@@ -0,0 +1,74 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
import subprocess
|
||||
import requests.certs
|
||||
|
||||
label = subprocess.check_output([
|
||||
"git", "describe", "--tags"]).strip()
|
||||
|
||||
with open('.version', 'w+') as f:
|
||||
f.write(label.decode())
|
||||
|
||||
block_cipher = None
|
||||
|
||||
added_files = [
|
||||
('../../imgs/gui/*.png', 'imgs/gui'),
|
||||
('../../imgs/gui/*.gif', 'imgs/gui'),
|
||||
('../../imgs/icons/*.png', 'imgs/icons'),
|
||||
('../../imgs/renders/*.png', 'imgs/renders'),
|
||||
('../../dist_assets/win/pyfa.ico', '.'),
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
(requests.certs.where(), '.'), # is this needed anymore?
|
||||
('../../eve.db', '.'),
|
||||
('../../README.md', '.'),
|
||||
('../../LICENSE', '.'),
|
||||
('../../.version', '.'),
|
||||
]
|
||||
|
||||
|
||||
import_these = []
|
||||
|
||||
# Walk directories that do dynamic importing
|
||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||
for file_ in files:
|
||||
if file_.endswith(".py") and not file_.startswith("_"):
|
||||
mod_name = "{}.{}".format(
|
||||
root.replace("/", "."),
|
||||
file_.split(".py")[0],
|
||||
)
|
||||
import_these.append(mod_name)
|
||||
|
||||
a = Analysis([r'../../pyfa.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=import_these,
|
||||
hookspath=['dist_assets/pyinstaller_hooks'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
name='pyfa',
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
runtime_tmpdir=None,
|
||||
console=False ,
|
||||
icon='dist_assets/mac/pyfa.icns',
|
||||
)
|
||||
|
||||
app = BUNDLE(exe,
|
||||
name='pyfa.app',
|
||||
icon=None,
|
||||
bundle_identifier=None)
|
||||
78
dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py
Normal file
78
dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# This apes hook-matplotlib.backends.py, but REMOVES backends, all but
|
||||
# the ones in the list below.
|
||||
# Courtesy of https://github.com/bpteague/cytoflow/blob/70f9291/packaging/hook-matplotlib.backends.py
|
||||
|
||||
KEEP = ["WXAgg", "WX", "agg"]
|
||||
|
||||
from PyInstaller.compat import is_darwin
|
||||
from PyInstaller.utils.hooks import (
|
||||
eval_statement, exec_statement, logger)
|
||||
|
||||
|
||||
def get_matplotlib_backend_module_names():
|
||||
"""
|
||||
List the names of all matplotlib backend modules importable under the
|
||||
current Python installation.
|
||||
Returns
|
||||
----------
|
||||
list
|
||||
List of the fully-qualified names of all such modules.
|
||||
"""
|
||||
# Statement safely importing a single backend module.
|
||||
import_statement = """
|
||||
import os, sys
|
||||
# Preserve stdout.
|
||||
sys_stdout = sys.stdout
|
||||
try:
|
||||
# Redirect output printed by this importation to "/dev/null", preventing
|
||||
# such output from being erroneously interpreted as an error.
|
||||
with open(os.devnull, 'w') as dev_null:
|
||||
sys.stdout = dev_null
|
||||
__import__('%s')
|
||||
# If this is an ImportError, print this exception's message without a traceback.
|
||||
# ImportError messages are human-readable and require no additional context.
|
||||
except ImportError as exc:
|
||||
sys.stdout = sys_stdout
|
||||
print(exc)
|
||||
# Else, print this exception preceded by a traceback. traceback.print_exc()
|
||||
# prints to stderr rather than stdout and must not be called here!
|
||||
except Exception:
|
||||
sys.stdout = sys_stdout
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
"""
|
||||
|
||||
# List of the human-readable names of all available backends.
|
||||
backend_names = eval_statement(
|
||||
'import matplotlib; print(matplotlib.rcsetup.all_backends)')
|
||||
|
||||
# List of the fully-qualified names of all importable backend modules.
|
||||
module_names = []
|
||||
|
||||
# If the current system is not OS X and the "CocoaAgg" backend is available,
|
||||
# remove this backend from consideration. Attempting to import this backend
|
||||
# on non-OS X systems halts the current subprocess without printing output
|
||||
# or raising exceptions, preventing its reliable detection.
|
||||
if not is_darwin and 'CocoaAgg' in backend_names:
|
||||
backend_names.remove('CocoaAgg')
|
||||
|
||||
# For safety, attempt to import each backend in a unique subprocess.
|
||||
for backend_name in backend_names:
|
||||
if backend_name in KEEP:
|
||||
continue
|
||||
|
||||
module_name = 'matplotlib.backends.backend_%s' % backend_name.lower()
|
||||
stdout = exec_statement(import_statement % module_name)
|
||||
|
||||
# If no output was printed, this backend is importable.
|
||||
if not stdout:
|
||||
module_names.append(module_name)
|
||||
logger.info(' Matplotlib backend "%s": removed' % backend_name)
|
||||
|
||||
return module_names
|
||||
|
||||
# Freeze all importable backends, as PyInstaller is unable to determine exactly
|
||||
# which backends are required by the current program.
|
||||
e=get_matplotlib_backend_module_names()
|
||||
print(e)
|
||||
excludedimports = e
|
||||
@@ -1,35 +1,37 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
# Note: This script is provided AS-IS for those that may be interested.
|
||||
# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment
|
||||
|
||||
# Command line to build:
|
||||
# (Run from directory where pyfa.py and pyfa.spec lives.)
|
||||
# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec
|
||||
|
||||
# Don't forget to change the path to where your pyfa.py and pyfa.spec lives
|
||||
# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'],
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
import subprocess
|
||||
import requests.certs
|
||||
|
||||
label = subprocess.check_output([
|
||||
"git", "describe", "--tags"]).strip()
|
||||
|
||||
with open('.version', 'w+') as f:
|
||||
f.write(label.decode())
|
||||
|
||||
block_cipher = None
|
||||
|
||||
added_files = [
|
||||
( 'imgs/gui/*.png', 'imgs/gui' ),
|
||||
( 'imgs/gui/*.gif', 'imgs/gui' ),
|
||||
( 'imgs/icons/*.png', 'imgs/icons' ),
|
||||
( 'imgs/renders/*.png', 'imgs/renders' ),
|
||||
( 'dist_assets/win/pyfa.ico', '.' ),
|
||||
( 'dist_assets/cacert.pem', '.' ),
|
||||
( 'eve.db', '.' ),
|
||||
( 'README.md', '.' ),
|
||||
( 'LICENSE', '.' ),
|
||||
('../../imgs/gui/*.png', 'imgs/gui'),
|
||||
('../../imgs/gui/*.gif', 'imgs/gui'),
|
||||
('../../imgs/icons/*.png', 'imgs/icons'),
|
||||
('../../imgs/renders/*.png', 'imgs/renders'),
|
||||
('../../service/jargon/*.yaml', 'service/jargon'),
|
||||
('../../dist_assets/win/pyfa.ico', '.'),
|
||||
(requests.certs.where(), '.'), # is this needed anymore?
|
||||
('../../eve.db', '.'),
|
||||
('../../README.md', '.'),
|
||||
('../../LICENSE', '.'),
|
||||
('../../.version', '.'),
|
||||
]
|
||||
|
||||
import_these = []
|
||||
|
||||
# Walk eos.effects and add all effects so we can import them properly
|
||||
for root, folders, files in os.walk("eos/effects"):
|
||||
# Walk directories that do dynamic importing
|
||||
paths = ('eos/effects', 'eos/db/migrations', 'service/conversions')
|
||||
for root, folders, files in chain.from_iterable(os.walk(path) for path in paths):
|
||||
for file_ in files:
|
||||
if file_.endswith(".py") and not file_.startswith("_"):
|
||||
mod_name = "{}.{}".format(
|
||||
@@ -38,25 +40,24 @@ for root, folders, files in os.walk("eos/effects"):
|
||||
)
|
||||
import_these.append(mod_name)
|
||||
|
||||
a = Analysis(
|
||||
['pyfa.py'],
|
||||
pathex=['C:\\projects\\pyfa\\'],
|
||||
a = Analysis(['../../pyfa.py'],
|
||||
pathex=[
|
||||
# Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566
|
||||
# To get this, download and install windows 10 SDK
|
||||
# If not building on Windows 10, this might be optional
|
||||
r'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=import_these,
|
||||
hookspath=[],
|
||||
hookspath=['dist_assets/pyinstaller_hooks'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
excludes=['Tkinter'],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
)
|
||||
cipher=block_cipher)
|
||||
|
||||
pyz = PYZ(
|
||||
a.pure,
|
||||
a.zipped_data,
|
||||
cipher=block_cipher,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
@@ -67,7 +68,6 @@ exe = EXE(pyz,
|
||||
upx=True,
|
||||
name='pyfa',
|
||||
icon='dist_assets/win/pyfa.ico',
|
||||
onefile=False,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
@@ -77,7 +77,6 @@ coll = COLLECT(
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
onefile=False,
|
||||
name='pyfa',
|
||||
icon='dist_assets/win/pyfa.ico',
|
||||
)
|
||||
)
|
||||
45
dist_assets/win/version_resource.py
Normal file
45
dist_assets/win/version_resource.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# UTF-8
|
||||
#
|
||||
# For more details about fixed file info 'ffi' see:
|
||||
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
|
||||
VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
|
||||
# Set not needed items to zero 0.
|
||||
filevers=(1, 15, 1, 0),
|
||||
prodvers=(1, 15, 1, 0),
|
||||
# Contains a bitmask that specifies the valid bits 'flags'r
|
||||
mask=0x3f,
|
||||
# Contains a bitmask that specifies the Boolean attributes of the file.
|
||||
flags=0x0,
|
||||
# The operating system for which this file was designed.
|
||||
# 0x4 - NT and there is no need to change it.
|
||||
OS=0x40004,
|
||||
# The general type of file.
|
||||
# 0x1 - the file is an application.
|
||||
fileType=0x1,
|
||||
# The function of the file.
|
||||
# 0x0 - the function is not defined for this fileType
|
||||
subtype=0x0,
|
||||
# Creation date and time stamp.
|
||||
date=(0, 0)
|
||||
),
|
||||
kids=[
|
||||
StringFileInfo(
|
||||
[
|
||||
StringTable(
|
||||
u'040904E4',
|
||||
[StringStruct(u'LegalCopyright', u''),
|
||||
StringStruct(u'InternalName', u'pyfa.exe'),
|
||||
StringStruct(u'FileVersion', u'1.15.1.0'),
|
||||
StringStruct(u'CompanyName', u''),
|
||||
StringStruct(u'OriginalFilename', u'pyfa.exe'),
|
||||
StringStruct(u'ProductVersion', u'1.15.1.0'),
|
||||
StringStruct(u'FileDescription', u'Python fitting assistant'),
|
||||
StringStruct(u'LegalTrademarks', u''),
|
||||
StringStruct(u'Comments', u''),
|
||||
StringStruct(u'ProductName', u'pyfa')])
|
||||
]),
|
||||
VarFileInfo([VarStruct(u'Translation', [1033, 1252])])
|
||||
]
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
import heapq
|
||||
import time
|
||||
from math import sqrt, exp
|
||||
from functools import reduce
|
||||
|
||||
DAY = 24 * 60 * 60 * 1000
|
||||
|
||||
@@ -88,7 +89,7 @@ class CapSimulator(object):
|
||||
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1
|
||||
|
||||
# Loop over grouped modules, configure staggering and push to the simulation state
|
||||
for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.iteritems():
|
||||
for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items():
|
||||
if self.stagger and not disableStagger:
|
||||
if clipSize == 0:
|
||||
duration = int(duration / amount)
|
||||
@@ -193,7 +194,7 @@ class CapSimulator(object):
|
||||
|
||||
# calculate EVE's stability value
|
||||
try:
|
||||
avgDrain = reduce(float.__add__, map(lambda x: x[2] / x[1], self.state), 0.0)
|
||||
avgDrain = reduce(float.__add__, [x[2] / x[1] for x in self.state], 0.0)
|
||||
self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2
|
||||
except ValueError:
|
||||
self.cap_stable_eve = 0.0
|
||||
|
||||
@@ -11,14 +11,14 @@ debug = False
|
||||
gamedataCache = True
|
||||
saveddataCache = True
|
||||
gamedata_version = ""
|
||||
gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding())
|
||||
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db"))
|
||||
pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring)
|
||||
|
||||
if istravis is True or hasattr(sys, '_called_from_test'):
|
||||
# Running in Travis. Run saveddata database in memory.
|
||||
saveddata_connectionstring = 'sqlite:///:memory:'
|
||||
else:
|
||||
saveddata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
|
||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db"))
|
||||
|
||||
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
||||
|
||||
@@ -28,4 +28,4 @@ settings = {
|
||||
}
|
||||
|
||||
# Autodetect path, only change if the autodetection bugs out.
|
||||
path = dirname(unicode(__file__, sys.getfilesystemencoding()))
|
||||
path = dirname(__file__)
|
||||
|
||||
@@ -22,7 +22,7 @@ import threading
|
||||
from sqlalchemy import MetaData, create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
import migration
|
||||
from . import migration
|
||||
from eos import config
|
||||
from logbook import Logger
|
||||
|
||||
@@ -76,7 +76,7 @@ sd_lock = threading.RLock()
|
||||
# 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, \
|
||||
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
|
||||
miscData, module, override, price, queries, skill, targetResists, user
|
||||
|
||||
# Import queries
|
||||
|
||||
@@ -81,7 +81,7 @@ def getItem(lookfor, eager=None):
|
||||
item = gamedata_session.query(Item).get(lookfor)
|
||||
else:
|
||||
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in itemNameMap:
|
||||
id = itemNameMap[lookfor]
|
||||
if eager is None:
|
||||
@@ -154,7 +154,7 @@ def getGroup(lookfor, eager=None):
|
||||
group = gamedata_session.query(Group).get(lookfor)
|
||||
else:
|
||||
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in groupNameMap:
|
||||
id = groupNameMap[lookfor]
|
||||
if eager is None:
|
||||
@@ -181,7 +181,7 @@ def getCategory(lookfor, eager=None):
|
||||
else:
|
||||
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
|
||||
Category.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in categoryNameMap:
|
||||
id = categoryNameMap[lookfor]
|
||||
if eager is None:
|
||||
@@ -210,7 +210,7 @@ def getMetaGroup(lookfor, eager=None):
|
||||
else:
|
||||
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
|
||||
MetaGroup.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
if lookfor in metaGroupNameMap:
|
||||
id = metaGroupNameMap[lookfor]
|
||||
if eager is None:
|
||||
@@ -245,7 +245,7 @@ def getMarketGroup(lookfor, eager=None):
|
||||
def getItemsByCategory(filter, where=None, eager=None):
|
||||
if isinstance(filter, int):
|
||||
filter = Category.ID == filter
|
||||
elif isinstance(filter, basestring):
|
||||
elif isinstance(filter, str):
|
||||
filter = Category.name == filter
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
@@ -257,7 +257,7 @@ def getItemsByCategory(filter, where=None, eager=None):
|
||||
|
||||
@cachedQuery(3, "where", "nameLike", "join")
|
||||
def searchItems(nameLike, where=None, join=None, eager=None):
|
||||
if not isinstance(nameLike, basestring):
|
||||
if not isinstance(nameLike, str):
|
||||
raise TypeError("Need string as argument")
|
||||
|
||||
if join is None:
|
||||
@@ -268,7 +268,7 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
||||
|
||||
items = gamedata_session.query(Item).options(*processEager(eager)).join(*join)
|
||||
for token in nameLike.split(' '):
|
||||
token_safe = u"%{0}%".format(sqlizeString(token))
|
||||
token_safe = "%{0}%".format(sqlizeString(token))
|
||||
if where is not None:
|
||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
|
||||
else:
|
||||
@@ -279,12 +279,12 @@ def searchItems(nameLike, where=None, join=None, eager=None):
|
||||
|
||||
@cachedQuery(3, "where", "nameLike", "join")
|
||||
def searchSkills(nameLike, where=None, eager=None):
|
||||
if not isinstance(nameLike, basestring):
|
||||
if not isinstance(nameLike, str):
|
||||
raise TypeError("Need string as argument")
|
||||
|
||||
items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category)
|
||||
for token in nameLike.split(' '):
|
||||
token_safe = u"%{0}%".format(sqlizeString(token))
|
||||
token_safe = "%{0}%".format(sqlizeString(token))
|
||||
if where is not None:
|
||||
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
|
||||
else:
|
||||
@@ -322,7 +322,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
||||
|
||||
@cachedQuery(1, "attr")
|
||||
def getAttributeInfo(attr, eager=None):
|
||||
if isinstance(attr, basestring):
|
||||
if isinstance(attr, str):
|
||||
filter = AttributeInfo.name == attr
|
||||
elif isinstance(attr, int):
|
||||
filter = AttributeInfo.ID == attr
|
||||
@@ -337,7 +337,7 @@ def getAttributeInfo(attr, eager=None):
|
||||
|
||||
@cachedQuery(1, "field")
|
||||
def getMetaData(field):
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, str):
|
||||
data = gamedata_session.query(MetaData).get(field)
|
||||
else:
|
||||
raise TypeError("Need string as argument")
|
||||
@@ -367,7 +367,7 @@ def getRequiredFor(itemID, attrMapping):
|
||||
|
||||
skillToLevelClauses = []
|
||||
|
||||
for attrSkill, attrLevel in attrMapping.iteritems():
|
||||
for attrSkill, attrLevel in attrMapping.items():
|
||||
skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel))
|
||||
|
||||
queryOr = or_(*skillToLevelClauses)
|
||||
|
||||
@@ -3,7 +3,7 @@ import shutil
|
||||
import time
|
||||
|
||||
import config
|
||||
import migrations
|
||||
from . import migrations
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -34,7 +34,7 @@ def update(saveddata_engine):
|
||||
|
||||
shutil.copyfile(config.saveDB, toFile)
|
||||
|
||||
for version in xrange(dbVersion, appVersion):
|
||||
for version in range(dbVersion, appVersion):
|
||||
func = migrations.updates[version + 1]
|
||||
if func:
|
||||
pyfalog.info("Applying database update: {0}", version + 1)
|
||||
|
||||
@@ -15,7 +15,27 @@ updates = {}
|
||||
appVersion = 0
|
||||
|
||||
prefix = __name__ + "."
|
||||
for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix):
|
||||
|
||||
# load modules to work based with and without pyinstaller
|
||||
# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
|
||||
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
|
||||
|
||||
# load modules using iter_modules()
|
||||
# (should find all filters in normal build, but not pyinstaller)
|
||||
module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)]
|
||||
|
||||
# special handling for PyInstaller
|
||||
importers = map(pkgutil.get_importer, __path__)
|
||||
toc = set()
|
||||
for i in importers:
|
||||
if hasattr(i, 'toc'):
|
||||
toc |= i.toc
|
||||
|
||||
for elm in toc:
|
||||
if elm.startswith(prefix):
|
||||
module_names.append(elm)
|
||||
|
||||
for modname in module_names:
|
||||
# loop through python files, extracting update number and function, and
|
||||
# adding it to a list
|
||||
modname_tail = modname.rsplit('.', 1)[-1]
|
||||
|
||||
@@ -91,7 +91,7 @@ def upgrade(saveddata_engine):
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN targetResistsID INTEGER;")
|
||||
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -108,7 +108,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -332,7 +332,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -60,7 +60,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -29,7 +29,7 @@ def upgrade(saveddata_engine):
|
||||
"targetResists": 2
|
||||
}
|
||||
|
||||
for table in tables.keys():
|
||||
for table in list(tables.keys()):
|
||||
|
||||
# midnight brain, there's probably a much more simple way to do this, but fuck it
|
||||
if tables[table] > 0:
|
||||
|
||||
@@ -4204,7 +4204,7 @@ conversion2 = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# First we want to get a list of fittings that are completely fitted out with subsystems
|
||||
oldItems = [str(x) for x in conversion2.iterkeys()]
|
||||
oldItems = [str(x) for x in conversion2.keys()]
|
||||
|
||||
# I can't figure out a way to get IN operator to work when supplying a list using a parameterized query. So I'm
|
||||
# doing it the shitty way by formatting the SQL string. Don't do this kids!
|
||||
@@ -4239,7 +4239,7 @@ def upgrade(saveddata_engine):
|
||||
# if something fails, fuck it, we tried. It'll default to the generic conversion below
|
||||
continue
|
||||
|
||||
for oldItem, newItem in conversion2.iteritems():
|
||||
for oldItem, newItem in conversion2.items():
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(newItem, oldItem))
|
||||
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
|
||||
@@ -133,7 +133,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -17,7 +17,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert ships
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -77,7 +77,7 @@ CONVERSIONS = {
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Convert modules
|
||||
for replacement_item, list in CONVERSIONS.iteritems():
|
||||
for replacement_item, list in CONVERSIONS.items():
|
||||
for retired_item in list:
|
||||
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
|
||||
@@ -12,7 +12,6 @@ __all__ = [
|
||||
"miscData",
|
||||
"targetResists",
|
||||
"override",
|
||||
"crest",
|
||||
"implantSet",
|
||||
"loadDefaultDatabaseValues"
|
||||
]
|
||||
|
||||
@@ -17,24 +17,24 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float, UniqueConstraint
|
||||
from sqlalchemy.orm import relation, mapper
|
||||
import datetime
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.db.saveddata.implant import charImplants_table
|
||||
from eos.effectHandlerHelpers import HandledImplantBoosterList
|
||||
from eos.effectHandlerHelpers import HandledImplantBoosterList, HandledSsoCharacterList
|
||||
from eos.saveddata.implant import Implant
|
||||
from eos.saveddata.user import User
|
||||
from eos.saveddata.character import Character, Skill
|
||||
from eos.saveddata.ssocharacter import SsoCharacter
|
||||
|
||||
|
||||
|
||||
|
||||
characters_table = Table("characters", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("name", String, nullable=False),
|
||||
Column("apiID", Integer),
|
||||
Column("apiKey", String),
|
||||
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),
|
||||
@@ -42,6 +42,28 @@ characters_table = Table("characters", saveddata_meta,
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
|
||||
sso_table = Table("ssoCharacter", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("client", String, nullable=False),
|
||||
Column("characterID", Integer, nullable=False),
|
||||
Column("characterName", String, nullable=False),
|
||||
Column("refreshToken", String, nullable=False),
|
||||
Column("accessToken", String, nullable=False),
|
||||
Column("accessTokenExpires", DateTime, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
UniqueConstraint('client', 'characterID', name='uix_client_characterID'),
|
||||
UniqueConstraint('client', 'characterName', name='uix_client_characterName')
|
||||
)
|
||||
|
||||
sso_character_map_table = Table("ssoCharacterMap", saveddata_meta,
|
||||
Column("characterID", ForeignKey("characters.ID"), primary_key=True),
|
||||
Column("ssoCharacterID", ForeignKey("ssoCharacter.ID"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
mapper(SsoCharacter, sso_table)
|
||||
|
||||
mapper(Character, characters_table,
|
||||
properties={
|
||||
"_Character__alphaCloneID": characters_table.c.alphaCloneID,
|
||||
@@ -63,5 +85,10 @@ mapper(Character, characters_table,
|
||||
primaryjoin=charImplants_table.c.charID == characters_table.c.ID,
|
||||
secondaryjoin=charImplants_table.c.implantID == Implant.ID,
|
||||
secondary=charImplants_table),
|
||||
"_Character__ssoCharacters" : relation(
|
||||
SsoCharacter,
|
||||
collection_class=HandledSsoCharacterList,
|
||||
backref='characters',
|
||||
secondary=sso_character_map_table)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# ===============================================================================
|
||||
# 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 Table, Column, Integer, String, DateTime
|
||||
from sqlalchemy.orm import mapper
|
||||
import datetime
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.crestchar import CrestChar
|
||||
|
||||
crest_table = Table("crest", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("name", String, nullable=False, unique=True),
|
||||
Column("refresh_token", String, nullable=False),
|
||||
# These records aren't updated. Instead, they are dropped and created, hence we don't have a modified field
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now))
|
||||
|
||||
mapper(CrestChar, crest_table)
|
||||
@@ -27,7 +27,7 @@ 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.ssocharacter import SsoCharacter
|
||||
from eos.saveddata.damagePattern import DamagePattern
|
||||
from eos.saveddata.targetResists import TargetResists
|
||||
from eos.saveddata.character import Character
|
||||
@@ -109,9 +109,9 @@ if configVal is True:
|
||||
if type not in queryCache:
|
||||
return
|
||||
functionCache = queryCache[type]
|
||||
for _, localCache in functionCache.iteritems():
|
||||
for _, localCache in functionCache.items():
|
||||
toDelete = set()
|
||||
for cacheKey, info in localCache.iteritems():
|
||||
for cacheKey, info in localCache.items():
|
||||
IDs = info[1]
|
||||
if ID in IDs:
|
||||
toDelete.add(cacheKey)
|
||||
@@ -156,7 +156,7 @@ def getUser(lookfor, eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
user = saveddata_session.query(User).options(*eager).filter(User.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
user = saveddata_session.query(User).options(*eager).filter(User.username == lookfor).first()
|
||||
@@ -175,7 +175,7 @@ def getCharacter(lookfor, eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(Character).options(*eager).filter(
|
||||
@@ -337,7 +337,7 @@ def clearPrices():
|
||||
|
||||
|
||||
def getMiscData(field):
|
||||
if isinstance(field, basestring):
|
||||
if isinstance(field, str):
|
||||
with sd_lock:
|
||||
data = saveddata_session.query(MiscData).get(field)
|
||||
else:
|
||||
@@ -391,7 +391,7 @@ def getDamagePattern(lookfor, eager=None):
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
|
||||
DamagePattern.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(DamagePattern).options(*eager).filter(
|
||||
@@ -412,7 +412,7 @@ def getTargetResists(lookfor, eager=None):
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
|
||||
TargetResists.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(TargetResists).options(*eager).filter(
|
||||
@@ -433,7 +433,7 @@ def getImplantSet(lookfor, eager=None):
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(
|
||||
TargetResists.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
elif isinstance(lookfor, str):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first()
|
||||
@@ -443,10 +443,10 @@ def getImplantSet(lookfor, eager=None):
|
||||
|
||||
|
||||
def searchFits(nameLike, where=None, eager=None):
|
||||
if not isinstance(nameLike, basestring):
|
||||
if not isinstance(nameLike, str):
|
||||
raise TypeError("Need string as argument")
|
||||
# Prepare our string for request
|
||||
nameLike = u"%{0}%".format(sqlizeString(nameLike))
|
||||
nameLike = "%{0}%".format(sqlizeString(nameLike))
|
||||
|
||||
# Add any extra components to the search to our where clause
|
||||
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
|
||||
@@ -467,29 +467,28 @@ def getProjectedFits(fitID):
|
||||
raise TypeError("Need integer as argument")
|
||||
|
||||
|
||||
def getCrestCharacters(eager=None):
|
||||
def getSsoCharacters(clientHash, eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
characters = saveddata_session.query(CrestChar).options(*eager).all()
|
||||
characters = saveddata_session.query(SsoCharacter).filter(SsoCharacter.client == clientHash).options(*eager).all()
|
||||
return characters
|
||||
|
||||
|
||||
@cachedQuery(CrestChar, 1, "lookfor")
|
||||
def getCrestCharacter(lookfor, eager=None):
|
||||
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
|
||||
def getSsoCharacter(lookfor, clientHash, eager=None):
|
||||
filter = SsoCharacter.client == clientHash
|
||||
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(CrestChar).get(lookfor)
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.name == lookfor).first()
|
||||
filter = and_(filter, SsoCharacter.ID == lookfor)
|
||||
elif isinstance(lookfor, str):
|
||||
filter = and_(filter, SsoCharacter.characterName == lookfor)
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(SsoCharacter).options(*eager).filter(filter).first()
|
||||
|
||||
return character
|
||||
|
||||
|
||||
@@ -515,8 +514,8 @@ def removeInvalid(fits):
|
||||
invalids = [f for f in fits if f.isInvalid]
|
||||
|
||||
if invalids:
|
||||
map(fits.remove, invalids)
|
||||
map(saveddata_session.delete, invalids)
|
||||
list(map(fits.remove, invalids))
|
||||
list(map(saveddata_session.delete, invalids))
|
||||
saveddata_session.commit()
|
||||
|
||||
return fits
|
||||
@@ -544,7 +543,7 @@ def commit():
|
||||
try:
|
||||
saveddata_session.commit()
|
||||
saveddata_session.flush()
|
||||
except Exception:
|
||||
except Exception as ex:
|
||||
saveddata_session.rollback()
|
||||
exc_info = sys.exc_info()
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
|
||||
|
||||
@@ -39,7 +39,7 @@ def processEager(eager):
|
||||
return tuple()
|
||||
else:
|
||||
l = []
|
||||
if isinstance(eager, basestring):
|
||||
if isinstance(eager, str):
|
||||
eager = (eager,)
|
||||
|
||||
for e in eager:
|
||||
@@ -50,7 +50,7 @@ def processEager(eager):
|
||||
|
||||
def _replacements(eagerString):
|
||||
splitEager = eagerString.split(".")
|
||||
for i in xrange(len(splitEager)):
|
||||
for i in range(len(splitEager)):
|
||||
part = splitEager[i]
|
||||
replacement = replace.get(part)
|
||||
if replacement:
|
||||
|
||||
@@ -115,7 +115,7 @@ class HandledList(list):
|
||||
class HandledModuleList(HandledList):
|
||||
def append(self, mod):
|
||||
emptyPosition = float("Inf")
|
||||
for i in xrange(len(self)):
|
||||
for i in range(len(self)):
|
||||
currMod = self[i]
|
||||
if currMod.isEmpty and not mod.isEmpty and currMod.slot == mod.slot:
|
||||
currPos = mod.position or i
|
||||
@@ -149,7 +149,7 @@ class HandledModuleList(HandledList):
|
||||
oldPos = mod.position
|
||||
|
||||
mod.position = None
|
||||
for i in xrange(oldPos, len(self)):
|
||||
for i in range(oldPos, len(self)):
|
||||
self[i].position -= 1
|
||||
|
||||
def toDummy(self, index):
|
||||
@@ -205,6 +205,16 @@ class HandledImplantBoosterList(HandledList):
|
||||
HandledList.append(self, thing)
|
||||
|
||||
|
||||
class HandledSsoCharacterList(list):
|
||||
def append(self, character):
|
||||
old = next((x for x in self if x.client == character.client), None)
|
||||
if old is not None:
|
||||
pyfalog.warning("Removing SSO Character with same hash: {}".format(repr(old)))
|
||||
list.remove(self, old)
|
||||
|
||||
list.append(self, character)
|
||||
|
||||
|
||||
class HandledProjectedModList(HandledList):
|
||||
def append(self, proj):
|
||||
if proj.isInvalid:
|
||||
|
||||
@@ -10,4 +10,6 @@ type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier"))
|
||||
# We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay).
|
||||
# It will return 0 as it doesn't exist, which would nullify whatever the target attribute is
|
||||
fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier", None))
|
||||
|
||||
@@ -6,6 +6,6 @@ type = "active"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
for x in xrange(1, 4):
|
||||
for x in range(1, 4):
|
||||
value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x))
|
||||
module.multiplyItemAttr("warfareBuff{}Value".format(x), value)
|
||||
|
||||
@@ -8,7 +8,7 @@ type = "passive"
|
||||
|
||||
def handler(fit, container, context):
|
||||
level = container.level if "skill" in context else 1
|
||||
for i in xrange(5):
|
||||
for i in range(5):
|
||||
attr = "boosterEffectChance{0}".format(i + 1)
|
||||
fit.boosters.filteredItemBoost(lambda booster: attr in booster.itemModifiedAttributes,
|
||||
attr, container.getModifiedItemAttr("boosterChanceBonus") * level)
|
||||
|
||||
@@ -16,7 +16,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -7,7 +7,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -7,7 +7,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -7,7 +7,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -7,7 +7,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -6,7 +6,7 @@ type = "active", "gang"
|
||||
|
||||
|
||||
def handler(fit, module, context, **kwargs):
|
||||
for x in xrange(1, 5):
|
||||
for x in range(1, 5):
|
||||
if module.getModifiedItemAttr("warfareBuff{}ID".format(x)):
|
||||
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
|
||||
id = module.getModifiedItemAttr("warfareBuff{}ID".format(x))
|
||||
|
||||
@@ -9,4 +9,6 @@ type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier"))
|
||||
# We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay).
|
||||
# It will return 0 as it doesn't exist, which would nullify whatever the target attribute is
|
||||
fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier", None))
|
||||
|
||||
@@ -9,4 +9,6 @@ type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier"))
|
||||
# We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay).
|
||||
# It will return 0 as it doesn't exist, which would nullify whatever the target attribute is
|
||||
fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier", None))
|
||||
|
||||
@@ -4,5 +4,5 @@ runTime = "late"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
for x in xrange(1, 4):
|
||||
for x in range(1, 4):
|
||||
module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus"))
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# warpDisruptSphere
|
||||
#
|
||||
# Used by:
|
||||
# Modules from group: Warp Disrupt Field Generator (7 of 7)
|
||||
|
||||
# warpDisruptSphere
|
||||
#
|
||||
|
||||
@@ -16,7 +16,10 @@ def handler(fit, module, context):
|
||||
|
||||
# this is such a dirty hack
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
if not mod.isEmpty and mod.state > State.ONLINE and (
|
||||
mod.item.requiresSkill("Micro Jump Drive Operation")
|
||||
or mod.item.requiresSkill("High Speed Maneuvering")
|
||||
):
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
|
||||
@@ -58,7 +58,7 @@ def rel_listener(target, value, initiator):
|
||||
if not target or (isinstance(value, Module) and value.isEmpty):
|
||||
return
|
||||
|
||||
print "{} has had a relationship change :D".format(target)
|
||||
print("{} has had a relationship change :D".format(target))
|
||||
target.modified = datetime.datetime.now()
|
||||
|
||||
|
||||
|
||||
@@ -22,13 +22,10 @@ import re
|
||||
from sqlalchemy.orm import reconstructor
|
||||
|
||||
import eos.db
|
||||
from eqBase import EqBase
|
||||
from .eqBase import EqBase
|
||||
from eos.saveddata.price import Price as types_Price
|
||||
from collections import OrderedDict
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
@@ -160,8 +157,6 @@ class Effect(EqBase):
|
||||
if it doesn't, set dummy values and add a dummy handler
|
||||
"""
|
||||
|
||||
pyfalog.debug("Generate effect handler for {}".format(self.name))
|
||||
|
||||
try:
|
||||
self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True)
|
||||
self.__handler = getattr(effectModule, "handler", effectDummy)
|
||||
@@ -258,7 +253,7 @@ class Item(EqBase):
|
||||
return default
|
||||
|
||||
def isType(self, type):
|
||||
for effect in self.effects.itervalues():
|
||||
for effect in self.effects.values():
|
||||
if effect.isType(type):
|
||||
return True
|
||||
|
||||
@@ -299,7 +294,7 @@ class Item(EqBase):
|
||||
self.__requiredSkills = requiredSkills
|
||||
# Map containing attribute IDs we may need for required skills
|
||||
# { requiredSkillX : requiredSkillXLevel }
|
||||
combinedAttrIDs = set(self.srqIDMap.iterkeys()).union(set(self.srqIDMap.itervalues()))
|
||||
combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values()))
|
||||
# Map containing result of the request
|
||||
# { attributeID : attributeValue }
|
||||
skillAttrs = {}
|
||||
@@ -309,7 +304,7 @@ class Item(EqBase):
|
||||
attrVal = attrInfo[2]
|
||||
skillAttrs[attrID] = attrVal
|
||||
# Go through all attributeID pairs
|
||||
for srqIDAtrr, srqLvlAttr in self.srqIDMap.iteritems():
|
||||
for srqIDAtrr, srqLvlAttr in self.srqIDMap.items():
|
||||
# Check if we have both in returned result
|
||||
if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs:
|
||||
skillID = int(skillAttrs[srqIDAtrr])
|
||||
@@ -383,7 +378,7 @@ class Item(EqBase):
|
||||
race = None
|
||||
# Check primary and secondary required skills' races
|
||||
if race is None:
|
||||
skillRaces = tuple(filter(lambda rid: rid, (s.raceID for s in tuple(self.requiredSkills.keys()))))
|
||||
skillRaces = tuple([rid for rid in (s.raceID for s in tuple(self.requiredSkills.keys())) if rid])
|
||||
if sum(skillRaces) in map:
|
||||
race = map[sum(skillRaces)]
|
||||
if race == "angelserp":
|
||||
@@ -405,7 +400,7 @@ class Item(EqBase):
|
||||
if self.__assistive is None:
|
||||
assistive = False
|
||||
# Go through all effects and find first assistive
|
||||
for effect in self.effects.itervalues():
|
||||
for effect in self.effects.values():
|
||||
if effect.isAssistance is True:
|
||||
# If we find one, stop and mark item as assistive
|
||||
assistive = True
|
||||
@@ -420,7 +415,7 @@ class Item(EqBase):
|
||||
if self.__offensive is None:
|
||||
offensive = False
|
||||
# Go through all effects and find first offensive
|
||||
for effect in self.effects.itervalues():
|
||||
for effect in self.effects.values():
|
||||
if effect.isOffensive is True:
|
||||
# If we find one, stop and mark item as offensive
|
||||
offensive = True
|
||||
@@ -429,8 +424,8 @@ class Item(EqBase):
|
||||
return self.__offensive
|
||||
|
||||
def requiresSkill(self, skill, level=None):
|
||||
for s, l in self.requiredSkills.iteritems():
|
||||
if isinstance(skill, basestring):
|
||||
for s, l in self.requiredSkills.items():
|
||||
if isinstance(skill, str):
|
||||
if s.name == skill and (level is None or l == level):
|
||||
return True
|
||||
|
||||
@@ -468,7 +463,7 @@ class Item(EqBase):
|
||||
return self.__price
|
||||
|
||||
def __repr__(self):
|
||||
return u"Item(ID={}, name={}) at {}".format(
|
||||
return "Item(ID={}, name={}) at {}".format(
|
||||
self.ID, self.name, hex(id(self))
|
||||
)
|
||||
|
||||
@@ -522,9 +517,9 @@ class Icon(EqBase):
|
||||
|
||||
class MarketGroup(EqBase):
|
||||
def __repr__(self):
|
||||
return u"MarketGroup(ID={}, name={}, parent={}) at {}".format(
|
||||
return "MarketGroup(ID={}, name={}, parent={}) at {}".format(
|
||||
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
|
||||
).encode('utf8')
|
||||
)
|
||||
|
||||
|
||||
class MetaGroup(EqBase):
|
||||
|
||||
@@ -25,7 +25,7 @@ class Graph(object):
|
||||
self.fit = fit
|
||||
self.data = {}
|
||||
if data is not None:
|
||||
for name, d in data.iteritems():
|
||||
for name, d in data.items():
|
||||
self.setData(Data(name, d))
|
||||
|
||||
self.function = function
|
||||
@@ -39,7 +39,7 @@ class Graph(object):
|
||||
def getIterator(self):
|
||||
pointNames = []
|
||||
pointIterators = []
|
||||
for data in self.data.itervalues():
|
||||
for data in self.data.values():
|
||||
pointNames.append(data.name)
|
||||
pointIterators.append(data)
|
||||
|
||||
@@ -48,7 +48,7 @@ class Graph(object):
|
||||
def _iterator(self, pointNames, pointIterators):
|
||||
for pointValues in itertools.product(*pointIterators):
|
||||
point = {}
|
||||
for i in xrange(len(pointValues)):
|
||||
for i in range(len(pointValues)):
|
||||
point[pointNames[i]] = pointValues[i]
|
||||
|
||||
yield point, self.function(point)
|
||||
@@ -61,12 +61,12 @@ class Data(object):
|
||||
self.data = self.parseString(dataString)
|
||||
|
||||
def parseString(self, dataString):
|
||||
if not isinstance(dataString, basestring):
|
||||
if not isinstance(dataString, str):
|
||||
return Constant(dataString),
|
||||
|
||||
dataList = []
|
||||
for data in dataString.split(";"):
|
||||
if isinstance(data, basestring) and "-" in data:
|
||||
if isinstance(data, str) and "-" in data:
|
||||
# Dealing with a range
|
||||
dataList.append(Range(data, self.step))
|
||||
else:
|
||||
@@ -85,7 +85,7 @@ class Data(object):
|
||||
|
||||
class Constant(object):
|
||||
def __init__(self, const):
|
||||
if isinstance(const, basestring):
|
||||
if isinstance(const, str):
|
||||
self.value = None if const == "" else float(const)
|
||||
else:
|
||||
self.value = const
|
||||
|
||||
@@ -63,10 +63,10 @@ class FitDpsGraph(Graph):
|
||||
ew['signatureRadius'].sort(key=abssort)
|
||||
ew['velocity'].sort(key=abssort)
|
||||
|
||||
for attr, values in ew.iteritems():
|
||||
for attr, values in ew.items():
|
||||
val = data[attr]
|
||||
try:
|
||||
for i in xrange(len(values)):
|
||||
for i in range(len(values)):
|
||||
bonus = values[i]
|
||||
val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289)
|
||||
data[attr] = val
|
||||
@@ -81,7 +81,7 @@ class FitDpsGraph(Graph):
|
||||
total += dps * self.calculateTurretMultiplier(mod, data)
|
||||
|
||||
elif mod.hardpoint == Hardpoint.MISSILE:
|
||||
if mod.state >= State.ACTIVE and mod.maxRange >= distance:
|
||||
if mod.state >= State.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance:
|
||||
total += dps * self.calculateMissileMultiplier(mod, data)
|
||||
|
||||
if distance <= fit.extraAttributes["droneControlRange"]:
|
||||
|
||||
@@ -28,23 +28,17 @@ cappingAttrKeyCache = {}
|
||||
|
||||
|
||||
class ItemAttrShortcut(object):
|
||||
def getModifiedItemAttr(self, key, default=None):
|
||||
def getModifiedItemAttr(self, key, default=0):
|
||||
return_value = self.itemModifiedAttributes.get(key)
|
||||
|
||||
if return_value is None and default is not None:
|
||||
return_value = default
|
||||
|
||||
return return_value
|
||||
return return_value or default
|
||||
|
||||
|
||||
class ChargeAttrShortcut(object):
|
||||
def getModifiedChargeAttr(self, key, default=None):
|
||||
def getModifiedChargeAttr(self, key, default=0):
|
||||
return_value = self.chargeModifiedAttributes.get(key)
|
||||
|
||||
if return_value is None and default is not None:
|
||||
return_value = default
|
||||
|
||||
return return_value
|
||||
return return_value or default
|
||||
|
||||
|
||||
class ModifiedAttributeDict(collections.MutableMapping):
|
||||
@@ -165,9 +159,9 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
|
||||
def __len__(self):
|
||||
keys = set()
|
||||
keys.update(self.original.iterkeys())
|
||||
keys.update(self.__modified.iterkeys())
|
||||
keys.update(self.__intermediary.iterkeys())
|
||||
keys.update(iter(self.original.keys()))
|
||||
keys.update(iter(self.__modified.keys()))
|
||||
keys.update(iter(self.__intermediary.keys()))
|
||||
return len(keys)
|
||||
|
||||
def __calculateValue(self, key):
|
||||
@@ -231,11 +225,11 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
val *= multiplier
|
||||
# Each group is penalized independently
|
||||
# Things in different groups will not be stack penalized between each other
|
||||
for penalizedMultipliers in penalizedMultiplierGroups.itervalues():
|
||||
for penalizedMultipliers in penalizedMultiplierGroups.values():
|
||||
# A quick explanation of how this works:
|
||||
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
|
||||
l1 = filter(lambda _val: _val > 1, penalizedMultipliers)
|
||||
l2 = filter(lambda _val: _val < 1, penalizedMultipliers)
|
||||
l1 = [_val for _val in penalizedMultipliers if _val > 1]
|
||||
l2 = [_val for _val in penalizedMultipliers if _val < 1]
|
||||
# 2: The most significant bonuses take the smallest penalty,
|
||||
# This means we'll have to sort
|
||||
abssort = lambda _val: -abs(_val - 1)
|
||||
@@ -245,7 +239,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
# Any module after the first takes penalties according to:
|
||||
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
|
||||
for l in (l1, l2):
|
||||
for i in xrange(len(l)):
|
||||
for i in range(len(l)):
|
||||
bonus = l[i]
|
||||
val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289)
|
||||
val += postIncrease
|
||||
@@ -388,7 +382,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
"""Force value to attribute and prohibit any changes to it"""
|
||||
self.__forced[attributeName] = value
|
||||
self.__placehold(attributeName)
|
||||
self.__afflict(attributeName, u"\u2263", value)
|
||||
self.__afflict(attributeName, "\u2263", value)
|
||||
|
||||
@staticmethod
|
||||
def getResistance(fit, effect):
|
||||
|
||||
@@ -116,7 +116,7 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
if not self.active:
|
||||
return
|
||||
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
(effect.isType("passive") or effect.isType("boosterSideEffect")):
|
||||
if effect.isType("boosterSideEffect") and effect not in self.activeSideEffectEffects:
|
||||
|
||||
@@ -39,7 +39,7 @@ class BoosterSideEffect(object):
|
||||
self.__effect = None
|
||||
|
||||
if self.effectID:
|
||||
self.__effect = next((x for x in self.booster.item.effects.itervalues() if x.ID == self.effectID), None)
|
||||
self.__effect = next((x for x in self.booster.item.effects.values() if x.ID == self.effectID), None)
|
||||
if self.__effect is None:
|
||||
pyfalog.error("Effect (id: {0}) does not exist", self.effectID)
|
||||
return
|
||||
|
||||
@@ -77,8 +77,8 @@ class Cargo(HandledItem, ItemAttrShortcut):
|
||||
"amount": lambda _val: isinstance(_val, int)
|
||||
}
|
||||
|
||||
if key == "amount" and val > sys.maxint:
|
||||
val = sys.maxint
|
||||
if key == "amount" and val > sys.maxsize:
|
||||
val = sys.maxsize
|
||||
|
||||
if not map[key](val):
|
||||
raise ValueError(str(val) + " is not a valid value for " + key)
|
||||
|
||||
@@ -119,16 +119,9 @@ class Character(object):
|
||||
|
||||
return all0
|
||||
|
||||
def apiUpdateCharSheet(self, skills, secStatus=0):
|
||||
for skillRow in skills:
|
||||
try:
|
||||
skill = self.getSkill(int(skillRow["typeID"]))
|
||||
skill.setLevel(int(skillRow["level"]), persist=True, ignoreRestrict=True)
|
||||
except:
|
||||
# if setting a skill doesn't work, it's not the end of the world, just quietly pass
|
||||
pass
|
||||
|
||||
self.secStatus = secStatus
|
||||
def clearSkills(self):
|
||||
del self.__skills[:]
|
||||
self.__skillIdMap.clear()
|
||||
|
||||
@property
|
||||
def ro(self):
|
||||
@@ -162,7 +155,7 @@ class Character(object):
|
||||
name += " *"
|
||||
|
||||
if self.alphaCloneID:
|
||||
name += u' (\u03B1)'
|
||||
name += ' (\u03B1)'
|
||||
|
||||
return name
|
||||
|
||||
@@ -170,6 +163,18 @@ class Character(object):
|
||||
def name(self, name):
|
||||
self.savedName = name
|
||||
|
||||
def setSsoCharacter(self, character, clientHash):
|
||||
if character is not None:
|
||||
self.__ssoCharacters.append(character)
|
||||
else:
|
||||
for x in self.__ssoCharacters:
|
||||
if x.client == clientHash:
|
||||
self.__ssoCharacters.remove(x)
|
||||
|
||||
|
||||
def getSsoCharacter(self, clientHash):
|
||||
return next((x for x in self.__ssoCharacters if x.client == clientHash), None)
|
||||
|
||||
@property
|
||||
def alphaCloneID(self):
|
||||
return self.__alphaCloneID
|
||||
@@ -199,7 +204,7 @@ class Character(object):
|
||||
del self.__skillIdMap[skill.itemID]
|
||||
|
||||
def getSkill(self, item):
|
||||
if isinstance(item, basestring):
|
||||
if isinstance(item, str):
|
||||
item = self.getSkillNameMap()[item]
|
||||
elif isinstance(item, int):
|
||||
item = self.getSkillIDMap()[item]
|
||||
@@ -282,7 +287,7 @@ class Character(object):
|
||||
map = {
|
||||
"ID" : lambda _val: isinstance(_val, int),
|
||||
"name" : lambda _val: True,
|
||||
"apiKey" : lambda _val: _val is None or (isinstance(_val, basestring) and len(_val) > 0),
|
||||
"apiKey" : lambda _val: _val is None or (isinstance(_val, str) and len(_val) > 0),
|
||||
"ownerID": lambda _val: isinstance(_val, int) or _val is None
|
||||
}
|
||||
|
||||
@@ -344,13 +349,13 @@ class Skill(HandledItem):
|
||||
elif self.character.name == "All 0":
|
||||
self.activeLevel = self.__level = 0
|
||||
elif self.character.alphaClone:
|
||||
return min(self.activeLevel, self.character.alphaClone.getSkillLevel(self)) or 0
|
||||
return min(self.activeLevel or 0, self.character.alphaClone.getSkillLevel(self) or 0)
|
||||
|
||||
return self.activeLevel or 0
|
||||
|
||||
def setLevel(self, level, persist=False, ignoreRestrict=False):
|
||||
|
||||
if (level < 0 or level > 5) and level is not None:
|
||||
if level is not None and (level < 0 or level > 5):
|
||||
raise ValueError(str(level) + " is not a valid value for level")
|
||||
|
||||
if hasattr(self, "_Skill__ro") and self.__ro is True:
|
||||
@@ -362,9 +367,9 @@ class Skill(HandledItem):
|
||||
# which affects performance. Should have a checkSkillLevels() or something that is more efficient for bulk.
|
||||
if not ignoreRestrict and eos.config.settings['strictSkillLevels']:
|
||||
start = time.time()
|
||||
for item, rlevel in self.item.requiredFor.iteritems():
|
||||
for item, rlevel in self.item.requiredFor.items():
|
||||
if item.group.category.ID == 16: # Skill category
|
||||
if level < rlevel:
|
||||
if level is None or level < rlevel:
|
||||
skill = self.character.getSkill(item.ID)
|
||||
# print "Removing skill: {}, Dependant level: {}, Required level: {}".format(skill, level, rlevel)
|
||||
skill.setLevel(None, persist)
|
||||
@@ -392,7 +397,7 @@ class Skill(HandledItem):
|
||||
if key in self.item.attributes:
|
||||
return self.item.attributes[key].value
|
||||
else:
|
||||
return None
|
||||
return 0
|
||||
|
||||
def calculateModifiedAttributes(self, fit, runTime):
|
||||
if self.__suppressed: # or not self.learned - removed for GH issue 101
|
||||
@@ -402,7 +407,7 @@ class Skill(HandledItem):
|
||||
if item is None:
|
||||
return
|
||||
|
||||
for effect in item.effects.itervalues():
|
||||
for effect in item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.isType("passive") and \
|
||||
(not fit.isStructure or effect.isType("structure")) and \
|
||||
|
||||
@@ -73,7 +73,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID")
|
||||
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
|
||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
|
||||
if chargeID is not None:
|
||||
charge = eos.db.getItem(int(chargeID))
|
||||
self.__charge = charge
|
||||
@@ -102,7 +103,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def cycleTime(self):
|
||||
return max(self.getModifiedItemAttr("duration"), 0)
|
||||
return max(self.getModifiedItemAttr("duration", 0), 0)
|
||||
|
||||
@property
|
||||
def dealsDamage(self):
|
||||
@@ -138,8 +139,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
cycleTime = self.getModifiedItemAttr(attr)
|
||||
|
||||
volley = sum(
|
||||
map(lambda d: (getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)),
|
||||
self.DAMAGE_TYPES))
|
||||
[(getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)) for d in self.DAMAGE_TYPES])
|
||||
volley *= self.amountActive
|
||||
volley *= self.getModifiedItemAttr("damageMultiplier") or 1
|
||||
self.__volley = volley
|
||||
@@ -155,7 +155,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
getter = self.getModifiedItemAttr
|
||||
|
||||
cycleTime = self.getModifiedItemAttr(attr)
|
||||
volley = sum(map(lambda d: getter(d), self.MINING_ATTRIBUTES)) * self.amountActive
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
@@ -168,7 +168,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"energyDestabilizationRange", "empFieldRange",
|
||||
"ecmBurstRange", "maxRange")
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
return maxRange
|
||||
if self.charge is not None:
|
||||
@@ -184,7 +184,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def falloff(self):
|
||||
attrs = ("falloff", "falloffEffectiveness")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
return falloff
|
||||
|
||||
@@ -236,7 +236,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
context = ("drone",)
|
||||
projected = False
|
||||
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
((projected is True and effect.isType("projected")) or
|
||||
@@ -251,7 +251,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
i += 1
|
||||
|
||||
if self.charge:
|
||||
for effect in self.charge.effects.itervalues():
|
||||
for effect in self.charge.effects.values():
|
||||
if effect.runTime == runTime and effect.activeByDefault:
|
||||
effect.handler(fit, self, ("droneCharge",))
|
||||
|
||||
@@ -263,8 +263,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
for i in xrange(1, 3):
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
|
||||
for i in range(1, 3):
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None)
|
||||
if groneGrp is not None:
|
||||
fitDroneGroupLimits.add(int(groneGrp))
|
||||
if len(fitDroneGroupLimits) == 0:
|
||||
|
||||
@@ -98,7 +98,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def __getAbilities(self):
|
||||
"""Returns list of FighterAbilities that are loaded with data"""
|
||||
return [FighterAbility(effect) for effect in self.item.effects.values()]
|
||||
return [FighterAbility(effect) for effect in list(self.item.effects.values())]
|
||||
|
||||
def __calculateSlot(self, item):
|
||||
types = {
|
||||
@@ -110,7 +110,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"StandupHeavy": Slot.FS_HEAVY
|
||||
}
|
||||
|
||||
for t, slot in types.iteritems():
|
||||
for t, slot in types.items():
|
||||
if self.getModifiedItemAttr("fighterSquadronIs{}".format(t)):
|
||||
return slot
|
||||
|
||||
@@ -202,12 +202,12 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"energyDestabilizationRange", "empFieldRange",
|
||||
"ecmBurstRange", "maxRange")
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
return maxRange
|
||||
if self.charge is not None:
|
||||
delay = self.getModifiedChargeAttr("explosionDelay")
|
||||
speed = self.getModifiedChargeAttr("maxVelocity")
|
||||
delay = self.getModifiedChargeAttr("explosionDelay", None)
|
||||
speed = self.getModifiedChargeAttr("maxVelocity", None)
|
||||
if delay is not None and speed is not None:
|
||||
return delay / 1000.0 * speed
|
||||
|
||||
@@ -218,7 +218,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def falloff(self):
|
||||
attrs = ("falloff", "falloffEffectiveness")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
return falloff
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class FighterAbility(object):
|
||||
self.__effect = None
|
||||
|
||||
if self.effectID:
|
||||
self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None)
|
||||
self.__effect = next((x for x in self.fighter.item.effects.values() if x.ID == self.effectID), None)
|
||||
if self.__effect is None:
|
||||
pyfalog.error("Effect (id: {0}) does not exist", self.effectID)
|
||||
return
|
||||
@@ -127,8 +127,8 @@ class FighterAbility(object):
|
||||
|
||||
if self.attrPrefix == "fighterAbilityLaunchBomb":
|
||||
# bomb calcs
|
||||
volley = sum(map(lambda attr: (self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * (
|
||||
1 - getattr(targetResists, "%sAmount" % attr, 0)), self.DAMAGE_TYPES))
|
||||
volley = sum([(self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * (
|
||||
1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES])
|
||||
else:
|
||||
volley = sum(map(lambda d2, d:
|
||||
(self.fighter.getModifiedItemAttr(
|
||||
|
||||
@@ -258,11 +258,11 @@ class Fit(object):
|
||||
def projectedFits(self):
|
||||
# only in extreme edge cases will the fit be invalid, but to be sure do
|
||||
# not return them.
|
||||
return [fit for fit in self.__projectedFits.values() if not fit.isInvalid]
|
||||
return [fit for fit in list(self.__projectedFits.values()) if not fit.isInvalid]
|
||||
|
||||
@property
|
||||
def commandFits(self):
|
||||
return [fit for fit in self.__commandFits.values() if not fit.isInvalid]
|
||||
return [fit for fit in list(self.__commandFits.values()) if not fit.isInvalid]
|
||||
|
||||
def getProjectionInfo(self, fitID):
|
||||
return self.projectedOnto.get(fitID, None)
|
||||
@@ -492,7 +492,7 @@ class Fit(object):
|
||||
|
||||
def __runCommandBoosts(self, runTime="normal"):
|
||||
pyfalog.debug("Applying gang boosts for {0}", repr(self))
|
||||
for warfareBuffID in self.commandBonuses.keys():
|
||||
for warfareBuffID in list(self.commandBonuses.keys()):
|
||||
# Unpack all data required to run effect properly
|
||||
effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID]
|
||||
|
||||
@@ -676,7 +676,7 @@ class Fit(object):
|
||||
|
||||
def __resetDependentCalcs(self):
|
||||
self.calculated = False
|
||||
for value in self.projectedOnto.values():
|
||||
for value in list(self.projectedOnto.values()):
|
||||
if value.victim_fit: # removing a self-projected fit causes victim fit to be None. @todo: look into why. :3
|
||||
value.victim_fit.calculated = False
|
||||
|
||||
@@ -707,14 +707,14 @@ class Fit(object):
|
||||
self.__resetDependentCalcs()
|
||||
|
||||
# For fits that are under local's Command, we do the same thing
|
||||
for value in self.boostedOnto.values():
|
||||
for value in list(self.boostedOnto.values()):
|
||||
# apparently this is a thing that happens when removing a command fit from a fit and then switching to
|
||||
# that command fit. Same as projected clears, figure out why.
|
||||
if value.boosted_fit:
|
||||
value.boosted_fit.__resetDependentCalcs()
|
||||
|
||||
if targetFit and type == CalcType.PROJECTED:
|
||||
pyfalog.debug(u"Calculating projections from {0} to target {1}", repr(self), repr(targetFit))
|
||||
pyfalog.debug("Calculating projections from {0} to target {1}", repr(self), repr(targetFit))
|
||||
projectionInfo = self.getProjectionInfo(targetFit.ID)
|
||||
|
||||
# Start applying any command fits that we may have.
|
||||
@@ -756,7 +756,7 @@ class Fit(object):
|
||||
|
||||
# Loop through our run times here. These determine which effects are run in which order.
|
||||
for runTime in ("early", "normal", "late"):
|
||||
pyfalog.debug("Run time: {0}", runTime)
|
||||
# pyfalog.debug("Run time: {0}", runTime)
|
||||
# Items that are unrestricted. These items are run on the local fit
|
||||
# first and then projected onto the target fit it one is designated
|
||||
u = [
|
||||
@@ -795,7 +795,7 @@ class Fit(object):
|
||||
# targetFit.register(item, origin=self)
|
||||
item.calculateModifiedAttributes(targetFit, runTime, False, True)
|
||||
|
||||
pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses))
|
||||
# pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses))
|
||||
|
||||
# If we are calculating our local or projected fit and have command bonuses, apply them
|
||||
if type != CalcType.COMMAND and self.commandBonuses:
|
||||
@@ -838,7 +838,7 @@ class Fit(object):
|
||||
for item in c:
|
||||
if item is not None:
|
||||
# apply effects onto target fit x amount of times
|
||||
for _ in xrange(projectionInfo.amount):
|
||||
for _ in range(projectionInfo.amount):
|
||||
targetFit.register(item, origin=self)
|
||||
item.calculateModifiedAttributes(targetFit, runTime, True)
|
||||
|
||||
@@ -854,7 +854,7 @@ class Fit(object):
|
||||
for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE):
|
||||
amount = self.getSlotsFree(slotType, True)
|
||||
if amount > 0:
|
||||
for _ in xrange(int(amount)):
|
||||
for _ in range(int(amount)):
|
||||
self.modules.append(Module.buildEmpty(slotType))
|
||||
|
||||
if amount < 0:
|
||||
@@ -870,7 +870,7 @@ class Fit(object):
|
||||
self.modules.remove(mod)
|
||||
|
||||
def unfill(self):
|
||||
for i in xrange(len(self.modules) - 1, -1, -1):
|
||||
for i in range(len(self.modules) - 1, -1, -1):
|
||||
mod = self.modules[i]
|
||||
if mod.isEmpty:
|
||||
del self.modules[i]
|
||||
@@ -878,7 +878,7 @@ class Fit(object):
|
||||
@property
|
||||
def modCount(self):
|
||||
x = 0
|
||||
for i in xrange(len(self.modules) - 1, -1, -1):
|
||||
for i in range(len(self.modules) - 1, -1, -1):
|
||||
mod = self.modules[i]
|
||||
if not mod.isEmpty:
|
||||
x += 1
|
||||
@@ -1531,11 +1531,11 @@ class Fit(object):
|
||||
return copy_ship
|
||||
|
||||
def __repr__(self):
|
||||
return u"Fit(ID={}, ship={}, name={}) at {}".format(
|
||||
return "Fit(ID={}, ship={}, name={}) at {}".format(
|
||||
self.ID, self.ship.item.name, self.name, hex(id(self))
|
||||
).encode('utf8')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return u"{} ({})".format(
|
||||
return "{} ({})".format(
|
||||
self.name, self.ship.item.name
|
||||
).encode('utf8')
|
||||
)
|
||||
|
||||
@@ -93,7 +93,7 @@ class Implant(HandledItem, ItemAttrShortcut):
|
||||
return
|
||||
if not self.active:
|
||||
return
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and effect.isType("passive") and effect.activeByDefault:
|
||||
effect.handler(fit, self, ("implant",))
|
||||
|
||||
|
||||
@@ -50,6 +50,6 @@ class Mode(ItemAttrShortcut, HandledItem):
|
||||
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False):
|
||||
if self.item:
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and effect.activeByDefault:
|
||||
effect.handler(fit, self, context=("module",))
|
||||
|
||||
@@ -182,7 +182,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@property
|
||||
def numShots(self):
|
||||
if self.charge is None:
|
||||
return None
|
||||
return 0
|
||||
if self.__chargeCycles is None and self.charge:
|
||||
numCharges = self.numCharges
|
||||
# Usual ammo like projectiles and missiles
|
||||
@@ -217,7 +217,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0
|
||||
shieldRep = self.getModifiedItemAttr("shieldBonus") or 0
|
||||
if not cycles or (not armorRep and not shieldRep):
|
||||
return None
|
||||
return 0
|
||||
hp = round((armorRep + shieldRep) * cycles)
|
||||
return hp
|
||||
|
||||
@@ -255,7 +255,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"ecmBurstRange", "warpScrambleRange", "cargoScanRange",
|
||||
"shipScanRange", "surveyScanRange")
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
return maxRange
|
||||
if self.charge is not None:
|
||||
@@ -284,7 +284,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def falloff(self):
|
||||
attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
return falloff
|
||||
|
||||
@@ -333,9 +333,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
else:
|
||||
func = self.getModifiedItemAttr
|
||||
|
||||
volley = sum(map(
|
||||
lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)),
|
||||
self.DAMAGE_TYPES))
|
||||
volley = sum([(func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES])
|
||||
volley *= self.getModifiedItemAttr("damageMultiplier") or 1
|
||||
if volley:
|
||||
cycleTime = self.cycleTime
|
||||
@@ -422,19 +420,19 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
fitsOnType = set()
|
||||
fitsOnGroup = set()
|
||||
|
||||
shipType = self.getModifiedItemAttr("fitsToShipType")
|
||||
shipType = self.getModifiedItemAttr("fitsToShipType", None)
|
||||
if shipType is not None:
|
||||
fitsOnType.add(shipType)
|
||||
|
||||
for attr in self.itemModifiedAttributes.keys():
|
||||
for attr in list(self.itemModifiedAttributes.keys()):
|
||||
if attr.startswith("canFitShipType"):
|
||||
shipType = self.getModifiedItemAttr(attr)
|
||||
shipType = self.getModifiedItemAttr(attr, None)
|
||||
if shipType is not None:
|
||||
fitsOnType.add(shipType)
|
||||
|
||||
for attr in self.itemModifiedAttributes.keys():
|
||||
for attr in list(self.itemModifiedAttributes.keys()):
|
||||
if attr.startswith("canFitShipGroup"):
|
||||
shipGroup = self.getModifiedItemAttr(attr)
|
||||
shipGroup = self.getModifiedItemAttr(attr, None)
|
||||
if shipGroup is not None:
|
||||
fitsOnGroup.add(shipGroup)
|
||||
|
||||
@@ -466,7 +464,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
|
||||
# Check max group fitted
|
||||
max = self.getModifiedItemAttr("maxGroupFitted")
|
||||
max = self.getModifiedItemAttr("maxGroupFitted", None)
|
||||
if max is not None:
|
||||
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
|
||||
for mod in fit.modules:
|
||||
@@ -479,11 +477,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check this only if we're told to do so
|
||||
if hardpointLimit:
|
||||
if self.hardpoint == Hardpoint.TURRET:
|
||||
if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1:
|
||||
if fit.ship.getModifiedItemAttr('turretSlotsLeft') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1:
|
||||
return False
|
||||
elif self.hardpoint == Hardpoint.MISSILE:
|
||||
if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed(
|
||||
Hardpoint.MISSILE) < 1:
|
||||
if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -513,7 +510,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return True
|
||||
|
||||
# Check if the local module is over it's max limit; if it's not, we're fine
|
||||
maxGroupActive = self.getModifiedItemAttr("maxGroupActive")
|
||||
maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None)
|
||||
if maxGroupActive is None and projectedOnto is None:
|
||||
return True
|
||||
|
||||
@@ -561,7 +558,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
chargeGroup = charge.groupID
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i))
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is None:
|
||||
continue
|
||||
if itemChargeGroup == chargeGroup:
|
||||
@@ -572,7 +569,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def getValidCharges(self):
|
||||
validCharges = set()
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i))
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is not None:
|
||||
g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes"))
|
||||
if g is None:
|
||||
@@ -593,7 +590,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if item is None:
|
||||
return Hardpoint.NONE
|
||||
|
||||
for effectName, slot in effectHardpointMap.iteritems():
|
||||
for effectName, slot in effectHardpointMap.items():
|
||||
if effectName in item.effects:
|
||||
return slot
|
||||
|
||||
@@ -611,7 +608,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
}
|
||||
if item is None:
|
||||
return None
|
||||
for effectName, slot in effectSlotMap.iteritems():
|
||||
for effectName, slot in effectSlotMap.items():
|
||||
if effectName in item.effects:
|
||||
return slot
|
||||
if item.group.name == "Effect Beacon":
|
||||
@@ -665,7 +662,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.charge is not None:
|
||||
# fix for #82 and it's regression #106
|
||||
if not projected or (self.projected and not forceProjected) or gang:
|
||||
for effect in self.charge.effects.itervalues():
|
||||
for effect in self.charge.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
(effect.isType("offline") or
|
||||
@@ -684,7 +681,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
if self.item:
|
||||
if self.state >= State.OVERHEATED:
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.isType("overheat") \
|
||||
and not forceProjected \
|
||||
@@ -692,7 +689,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
and ((gang and effect.isType("gang")) or not gang):
|
||||
effect.handler(fit, self, context)
|
||||
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
(effect.isType("offline") or
|
||||
@@ -752,13 +749,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@property
|
||||
def rawCycleTime(self):
|
||||
speed = max(
|
||||
self.getModifiedItemAttr("speed"), # Most weapons
|
||||
self.getModifiedItemAttr("duration"), # Most average modules
|
||||
self.getModifiedItemAttr("durationSensorDampeningBurstProjector"),
|
||||
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector"),
|
||||
self.getModifiedItemAttr("durationECMJammerBurstProjector"),
|
||||
self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector"),
|
||||
0, # Return 0 if none of the above are valid
|
||||
self.getModifiedItemAttr("speed", 0), # Most weapons
|
||||
self.getModifiedItemAttr("duration", 0), # Most average modules
|
||||
self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationECMJammerBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector", 0)
|
||||
)
|
||||
return speed
|
||||
|
||||
@@ -793,7 +789,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def __repr__(self):
|
||||
if self.item:
|
||||
return u"Module(ID={}, name={}) at {}".format(
|
||||
return "Module(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -87,7 +87,7 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False):
|
||||
if forceProjected:
|
||||
return
|
||||
for effect in self.item.effects.itervalues():
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.isType("passive") and \
|
||||
effect.activeByDefault:
|
||||
|
||||
@@ -18,17 +18,40 @@
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy.orm import reconstructor
|
||||
|
||||
import datetime
|
||||
import time
|
||||
|
||||
# from tomorrow import threads
|
||||
|
||||
|
||||
class CrestChar(object):
|
||||
def __init__(self, id, name, refresh_token=None):
|
||||
self.ID = id
|
||||
self.name = name
|
||||
self.refresh_token = refresh_token
|
||||
class SsoCharacter(object):
|
||||
def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
|
||||
self.characterID = charID
|
||||
self.characterName = name
|
||||
self.client = client
|
||||
self.accessToken = accessToken
|
||||
self.refreshToken = refreshToken
|
||||
self.accessTokenExpires = None
|
||||
self.esi_client = None
|
||||
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
pass
|
||||
self.esi_client = None
|
||||
|
||||
def get_sso_data(self):
|
||||
""" Little "helper" function to get formated data for esipy security
|
||||
"""
|
||||
return {
|
||||
'access_token': self.accessToken,
|
||||
'refresh_token': self.refreshToken,
|
||||
'expires_in': (
|
||||
self.accessTokenExpires - datetime.datetime.utcnow()
|
||||
).total_seconds()
|
||||
}
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "SsoCharacter(ID={}, name={}, client={}) at {}".format(
|
||||
self.ID, self.characterName, self.client, hex(id(self))
|
||||
)
|
||||
@@ -33,7 +33,7 @@ class User(object):
|
||||
|
||||
def encodeAndSetPassword(self, pw):
|
||||
h = hashlib.new("sha256")
|
||||
salt = "".join([random.choice(string.letters) for _ in xrange(32)])
|
||||
salt = "".join([random.choice(string.letters) for _ in range(32)])
|
||||
h.update(pw)
|
||||
h.update(salt)
|
||||
self.password = ("%s%s" % (h.hexdigest(), salt))
|
||||
@@ -45,14 +45,14 @@ class User(object):
|
||||
h = hashlib.new("sha256")
|
||||
h.update(pw)
|
||||
h.update(salt)
|
||||
return self.password == (u"%s%s" % (h.hexdigest(), salt))
|
||||
return self.password == ("%s%s" % (h.hexdigest(), salt))
|
||||
|
||||
@validates("ID", "username", "password", "admin")
|
||||
def validator(self, key, val):
|
||||
map = {
|
||||
"ID" : lambda _val: isinstance(_val, int),
|
||||
"username": lambda _val: isinstance(_val, basestring),
|
||||
"password": lambda _val: isinstance(_val, basestring) and len(_val) == 96,
|
||||
"username": lambda _val: isinstance(_val, str),
|
||||
"password": lambda _val: isinstance(_val, str) and len(_val) == 96,
|
||||
"admin" : lambda _val: isinstance(_val, bool)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinAdditionPanes.boosterView import BoosterView
|
||||
from gui.builtinAdditionPanes.cargoView import CargoView
|
||||
from gui.builtinAdditionPanes.commandView import CommandView
|
||||
@@ -29,22 +29,22 @@ from gui.builtinAdditionPanes.fighterView import FighterView
|
||||
from gui.builtinAdditionPanes.implantView import ImplantView
|
||||
from gui.builtinAdditionPanes.notesView import NotesView
|
||||
from gui.builtinAdditionPanes.projectedView import ProjectedView
|
||||
from gui.chromeTabs import PFNotebook
|
||||
from gui.pyfatogglepanel import TogglePanel
|
||||
from gui.chrome_tabs import ChromeNotebook
|
||||
from gui.toggle_panel import TogglePanel
|
||||
|
||||
|
||||
class AdditionsPane(TogglePanel):
|
||||
def __init__(self, parent):
|
||||
|
||||
TogglePanel.__init__(self, parent, forceLayout=1)
|
||||
TogglePanel.__init__(self, parent, force_layout=1)
|
||||
|
||||
self.SetLabel("Additions")
|
||||
pane = self.GetContentPane()
|
||||
pane = self.GetContentPanel()
|
||||
|
||||
baseSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
pane.SetSizer(baseSizer)
|
||||
|
||||
self.notebook = PFNotebook(pane, False)
|
||||
self.notebook = ChromeNotebook(pane, False)
|
||||
self.notebook.SetMinSize((-1, 1000))
|
||||
|
||||
baseSizer.Add(self.notebook, 1, wx.EXPAND)
|
||||
@@ -59,28 +59,28 @@ class AdditionsPane(TogglePanel):
|
||||
notesImg = BitmapLoader.getImage("skill_small", "gui")
|
||||
|
||||
self.drone = DroneView(self.notebook)
|
||||
self.notebook.AddPage(self.drone, "Drones", tabImage=droneImg, showClose=False)
|
||||
self.notebook.AddPage(self.drone, "Drones", image=droneImg, closeable=False)
|
||||
|
||||
self.fighter = FighterView(self.notebook)
|
||||
self.notebook.AddPage(self.fighter, "Fighters", tabImage=fighterImg, showClose=False)
|
||||
self.notebook.AddPage(self.fighter, "Fighters", image=fighterImg, closeable=False)
|
||||
|
||||
self.cargo = CargoView(self.notebook)
|
||||
self.notebook.AddPage(self.cargo, "Cargo", tabImage=cargoImg, showClose=False)
|
||||
self.notebook.AddPage(self.cargo, "Cargo", image=cargoImg, closeable=False)
|
||||
|
||||
self.implant = ImplantView(self.notebook)
|
||||
self.notebook.AddPage(self.implant, "Implants", tabImage=implantImg, showClose=False)
|
||||
self.notebook.AddPage(self.implant, "Implants", image=implantImg, closeable=False)
|
||||
|
||||
self.booster = BoosterView(self.notebook)
|
||||
self.notebook.AddPage(self.booster, "Boosters", tabImage=boosterImg, showClose=False)
|
||||
self.notebook.AddPage(self.booster, "Boosters", image=boosterImg, closeable=False)
|
||||
|
||||
self.projectedPage = ProjectedView(self.notebook)
|
||||
self.notebook.AddPage(self.projectedPage, "Projected", tabImage=projectedImg, showClose=False)
|
||||
self.notebook.AddPage(self.projectedPage, "Projected", image=projectedImg, closeable=False)
|
||||
|
||||
self.gangPage = CommandView(self.notebook)
|
||||
self.notebook.AddPage(self.gangPage, "Command", tabImage=gangImg, showClose=False)
|
||||
self.notebook.AddPage(self.gangPage, "Command", image=gangImg, closeable=False)
|
||||
|
||||
self.notes = NotesView(self.notebook)
|
||||
self.notebook.AddPage(self.notes, "Notes", tabImage=notesImg, showClose=False)
|
||||
self.notebook.AddPage(self.notes, "Notes", image=notesImg, closeable=False)
|
||||
|
||||
self.notebook.SetSelection(0)
|
||||
|
||||
@@ -92,19 +92,16 @@ class AdditionsPane(TogglePanel):
|
||||
def getName(self, idx):
|
||||
return self.PANES[idx]
|
||||
|
||||
def toggleContent(self, event):
|
||||
TogglePanel.toggleContent(self, event)
|
||||
h = self.headerPanel.GetSize()[1] + 4
|
||||
def ToggleContent(self, event):
|
||||
TogglePanel.ToggleContent(self, event)
|
||||
h = self.header_panel.GetSize()[1] + 4
|
||||
|
||||
if self.IsCollapsed():
|
||||
self.old_pos = self.parent.GetSashPosition()
|
||||
self.parent.SetMinimumPaneSize(h)
|
||||
self.parent.SetSashPosition(h * -1, True)
|
||||
# only available in >= wx2.9
|
||||
if getattr(self.parent, "SetSashInvisible", None):
|
||||
self.parent.SetSashInvisible(True)
|
||||
self.parent.SetSashInvisible(True)
|
||||
else:
|
||||
if getattr(self.parent, "SetSashInvisible", None):
|
||||
self.parent.SetSashInvisible(False)
|
||||
self.parent.SetSashInvisible(False)
|
||||
self.parent.SetMinimumPaneSize(200)
|
||||
self.parent.SetSashPosition(self.old_pos, True)
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
import cStringIO
|
||||
import io
|
||||
import os.path
|
||||
import zipfile
|
||||
from collections import OrderedDict
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
@@ -29,23 +30,18 @@ import config
|
||||
from logbook import Logger
|
||||
logging = Logger(__name__)
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
|
||||
|
||||
class BitmapLoader(object):
|
||||
try:
|
||||
archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
|
||||
logging.info("Using zipped image files.")
|
||||
except IOError:
|
||||
except (IOError, TypeError):
|
||||
logging.info("Using local image files.")
|
||||
archive = None
|
||||
|
||||
cachedBitmaps = OrderedDict()
|
||||
dontUseCachedBitmaps = False
|
||||
max_bmps = 500
|
||||
cached_bitmaps = OrderedDict()
|
||||
dont_use_cached_bitmaps = False
|
||||
max_cached_bitmaps = 500
|
||||
|
||||
@classmethod
|
||||
def getStaticBitmap(cls, name, parent, location):
|
||||
@@ -55,25 +51,25 @@ class BitmapLoader(object):
|
||||
|
||||
@classmethod
|
||||
def getBitmap(cls, name, location):
|
||||
if cls.dontUseCachedBitmaps:
|
||||
if cls.dont_use_cached_bitmaps:
|
||||
img = cls.getImage(name, location)
|
||||
if img is not None:
|
||||
return img.ConvertToBitmap()
|
||||
|
||||
path = "%s%s" % (name, location)
|
||||
|
||||
if len(cls.cachedBitmaps) == cls.max_bmps:
|
||||
cls.cachedBitmaps.popitem(False)
|
||||
if len(cls.cached_bitmaps) == cls.max_cached_bitmaps:
|
||||
cls.cached_bitmaps.popitem(False)
|
||||
|
||||
if path not in cls.cachedBitmaps:
|
||||
if path not in cls.cached_bitmaps:
|
||||
img = cls.getImage(name, location)
|
||||
if img is not None:
|
||||
bmp = img.ConvertToBitmap()
|
||||
else:
|
||||
bmp = None
|
||||
cls.cachedBitmaps[path] = bmp
|
||||
cls.cached_bitmaps[path] = bmp
|
||||
else:
|
||||
bmp = cls.cachedBitmaps[path]
|
||||
bmp = cls.cached_bitmaps[path]
|
||||
|
||||
return bmp
|
||||
|
||||
@@ -88,14 +84,14 @@ class BitmapLoader(object):
|
||||
|
||||
try:
|
||||
img_data = cls.archive.read(path)
|
||||
sbuf = cStringIO.StringIO(img_data)
|
||||
sbuf = io.StringIO(img_data)
|
||||
return wx.ImageFromStream(sbuf)
|
||||
except KeyError:
|
||||
print("Missing icon file from zip: {0}".format(path))
|
||||
print(("Missing icon file from zip: {0}".format(path)))
|
||||
else:
|
||||
path = os.path.join(config.pyfaPath, 'imgs' + os.sep + location + os.sep + filename)
|
||||
|
||||
if os.path.exists(path):
|
||||
return wx.Image(path)
|
||||
else:
|
||||
print("Missing icon file: {0}".format(path))
|
||||
print(("Missing icon file: {0}".format(path)))
|
||||
@@ -28,12 +28,12 @@ from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class BoosterViewDrop(wx.PyDropTarget):
|
||||
class BoosterViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(BoosterViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -131,6 +131,7 @@ class BoosterView(d.Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addBooster(fitID, event.itemID)
|
||||
|
||||
@@ -28,12 +28,12 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class CargoViewDrop(wx.PyDropTarget):
|
||||
class CargoViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(CargoViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -88,7 +88,7 @@ class CargoView(d.Display):
|
||||
row = event.GetIndex()
|
||||
|
||||
if row != -1:
|
||||
data = wx.PyTextDataObject()
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "cargo:" + str(row)
|
||||
data.SetText(dataStr)
|
||||
|
||||
@@ -119,14 +119,14 @@ class CargoView(d.Display):
|
||||
module = fit.modules[modIdx]
|
||||
|
||||
if dstRow != -1: # we're swapping with cargo
|
||||
if mstate.CmdDown(): # if copying, append to cargo
|
||||
if mstate.cmdDown: # if copying, append to cargo
|
||||
sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID)
|
||||
else: # else, move / swap
|
||||
sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow)
|
||||
else: # dragging to blank spot, append
|
||||
sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID)
|
||||
|
||||
if not mstate.CmdDown(): # if not copying, remove module
|
||||
if not mstate.cmdDown: # if not copying, remove module
|
||||
sFit.removeModule(self.mainFrame.getActiveFit(), module.position)
|
||||
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="moddel", typeID=module.item.ID))
|
||||
|
||||
@@ -43,12 +43,12 @@ class DummyEntry(object):
|
||||
self.item = DummyItem(txt)
|
||||
|
||||
|
||||
class CommandViewDrop(wx.PyDropTarget):
|
||||
class CommandViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(CommandViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -119,7 +119,7 @@ class CommandView(d.Display):
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1 and isinstance(self.get(row), es_Drone):
|
||||
data = wx.PyTextDataObject()
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "command:" + str(self.GetItemData(row))
|
||||
data.SetText(dataStr)
|
||||
|
||||
@@ -136,6 +136,8 @@ class CommandView(d.Display):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(event.fitID)
|
||||
|
||||
CommandFits.populateFits(event)
|
||||
|
||||
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
|
||||
|
||||
# Clear list and get out if current fitId is None
|
||||
@@ -167,6 +169,8 @@ class CommandView(d.Display):
|
||||
|
||||
self.update(stuff)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def get(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
|
||||
from gui.display import Display
|
||||
from gui.builtinViewColumns.state import State
|
||||
@@ -30,12 +31,12 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class DroneViewDrop(wx.PyDropTarget):
|
||||
class DroneViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(DroneViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -66,6 +67,8 @@ class DroneView(Display):
|
||||
self.hoveredRow = None
|
||||
self.hoveredColumn = None
|
||||
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
@@ -101,7 +104,7 @@ class DroneView(Display):
|
||||
if self.DEFAULT_COLS[col] == "Miscellanea":
|
||||
tooltip = self.activeColumns[col].getToolTip(mod)
|
||||
if tooltip is not None:
|
||||
self.SetToolTipString(tooltip)
|
||||
self.SetToolTip(tooltip)
|
||||
else:
|
||||
self.SetToolTip(None)
|
||||
else:
|
||||
@@ -123,7 +126,7 @@ class DroneView(Display):
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1:
|
||||
data = wx.PyTextDataObject()
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "drone:" + str(row)
|
||||
data.SetText(dataStr)
|
||||
|
||||
@@ -207,6 +210,7 @@ class DroneView(Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addDrone(fitID, event.itemID)
|
||||
|
||||
@@ -32,12 +32,12 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class FighterViewDrop(wx.PyDropTarget):
|
||||
class FighterViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(FighterViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -60,7 +60,7 @@ class FighterView(wx.Panel):
|
||||
mainSizer.Add(self.fighterDisplay, 1, wx.EXPAND, 0)
|
||||
|
||||
textSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
textSizer.AddStretchSpacer()
|
||||
|
||||
for x in self.labels:
|
||||
lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize())
|
||||
@@ -75,7 +75,7 @@ class FighterView(wx.Panel):
|
||||
lbl = wx.StaticText(self, wx.ID_ANY, "0")
|
||||
setattr(self, "label%sTotal" % (x.capitalize()), lbl)
|
||||
textSizer.Add(lbl, 0, wx.ALIGN_CENTER)
|
||||
textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
textSizer.AddStretchSpacer()
|
||||
|
||||
mainSizer.Add(textSizer, 0, wx.EXPAND, 5)
|
||||
|
||||
@@ -97,7 +97,7 @@ class FighterView(wx.Panel):
|
||||
slot = getattr(Slot, "F_{}".format(x.upper()))
|
||||
used = fit.getSlotsUsed(slot)
|
||||
total = fit.getNumSlots(slot)
|
||||
color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour(
|
||||
color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings.GetColour(
|
||||
wx.SYS_COLOUR_WINDOWTEXT)
|
||||
|
||||
lbl = getattr(self, "label%sUsed" % x.capitalize())
|
||||
@@ -110,6 +110,8 @@ class FighterView(wx.Panel):
|
||||
|
||||
self.Refresh()
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
class FighterDisplay(d.Display):
|
||||
DEFAULT_COLS = ["State",
|
||||
@@ -166,7 +168,7 @@ class FighterDisplay(d.Display):
|
||||
if self.DEFAULT_COLS[col] == "Miscellanea":
|
||||
tooltip = self.activeColumns[col].getToolTip(mod)
|
||||
if tooltip is not None:
|
||||
self.SetToolTipString(tooltip)
|
||||
self.SetToolTip(tooltip)
|
||||
else:
|
||||
self.SetToolTip(None)
|
||||
else:
|
||||
@@ -188,7 +190,7 @@ class FighterDisplay(d.Display):
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1:
|
||||
data = wx.PyTextDataObject()
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "fighter:" + str(row)
|
||||
data.SetText(dataStr)
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ class ImplantView(wx.Panel):
|
||||
mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0)
|
||||
|
||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
radioSizer.AddStretchSpacer()
|
||||
self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP)
|
||||
self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants")
|
||||
radioSizer.Add(self.rbFit, 0, wx.ALL, 5)
|
||||
radioSizer.Add(self.rbChar, 0, wx.ALL, 5)
|
||||
radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
radioSizer.AddStretchSpacer()
|
||||
|
||||
mainSizer.Add(radioSizer, 0, wx.EXPAND, 5)
|
||||
|
||||
@@ -71,6 +71,8 @@ class ImplantView(wx.Panel):
|
||||
self.rbFit.Enable(fit is not None)
|
||||
self.rbChar.Enable(fit is not None)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def OnRadioSelect(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
@@ -150,6 +152,7 @@ class ImplantDisplay(d.Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addImplant(fitID, event.itemID)
|
||||
|
||||
@@ -40,6 +40,8 @@ class NotesView(wx.Panel):
|
||||
self.lastFitId = event.fitID
|
||||
self.editNotes.SetValue(fit.notes or "")
|
||||
|
||||
event.Skip()
|
||||
|
||||
def onText(self, event):
|
||||
# delay the save so we're not writing to sqlite on every keystroke
|
||||
self.saveTimer.Stop() # cancel the existing timer
|
||||
|
||||
@@ -47,12 +47,12 @@ class DummyEntry(object):
|
||||
self.item = DummyItem(txt)
|
||||
|
||||
|
||||
class ProjectedViewDrop(wx.PyDropTarget):
|
||||
class ProjectedViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(ProjectedViewDrop, self).__init__(*args, **kwargs)
|
||||
self.dropFn = dropFn
|
||||
# this is really transferring an EVE itemID
|
||||
self.dropData = wx.PyTextDataObject()
|
||||
self.dropData = wx.TextDataObject()
|
||||
self.SetDataObject(self.dropData)
|
||||
|
||||
def OnData(self, x, y, t):
|
||||
@@ -139,7 +139,7 @@ class ProjectedView(d.Display):
|
||||
def startDrag(self, event):
|
||||
row = event.GetIndex()
|
||||
if row != -1 and isinstance(self.get(row), es_Drone):
|
||||
data = wx.PyTextDataObject()
|
||||
data = wx.TextDataObject()
|
||||
dataStr = "projected:" + str(self.GetItemData(row))
|
||||
data.SetText(dataStr)
|
||||
|
||||
@@ -229,6 +229,8 @@ class ProjectedView(d.Display):
|
||||
|
||||
self.update(stuff)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def get(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
@@ -3,7 +3,7 @@ from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from eos.saveddata.character import Skill
|
||||
import gui.globalEvents as GE
|
||||
from service.fit import Fit
|
||||
@@ -49,7 +49,7 @@ class ChangeAffectingSkills(ContextMenu):
|
||||
if cont[attrName] == 0:
|
||||
continue
|
||||
|
||||
for fit, afflictors in cont.getAfflictions(attrName).iteritems():
|
||||
for fit, afflictors in cont.getAfflictions(attrName).items():
|
||||
for afflictor, modifier, amount, used in afflictors:
|
||||
# only add Skills
|
||||
if not isinstance(afflictor, Skill):
|
||||
@@ -89,12 +89,12 @@ class ChangeAffectingSkills(ContextMenu):
|
||||
if bitmap is not None:
|
||||
skillItem.SetBitmap(bitmap)
|
||||
|
||||
for i in xrange(-1, 6):
|
||||
for i in range(-1, 6):
|
||||
levelItem = self.addSkill(rootMenu if msw else grandSub, skill, i)
|
||||
grandSub.AppendItem(levelItem)
|
||||
grandSub.Append(levelItem)
|
||||
if (not skill.learned and i == -1) or (skill.learned and skill.level == i):
|
||||
levelItem.Check(True)
|
||||
sub.AppendItem(skillItem)
|
||||
sub.Append(skillItem)
|
||||
|
||||
return sub
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ class CommandFits(ContextMenu):
|
||||
if evt is None or not ids.isdisjoint(cls.commandTypeIDs):
|
||||
# we are adding or removing an item that defines a command fit. Need to refresh fit list
|
||||
cls.populateFits(evt)
|
||||
evt.Skip()
|
||||
|
||||
@classmethod
|
||||
def populateFits(cls, evt):
|
||||
@@ -50,7 +51,7 @@ class CommandFits(ContextMenu):
|
||||
return "Command Fits"
|
||||
|
||||
def addFit(self, menu, fit, includeShip=False):
|
||||
label = fit.name if not includeShip else u"({}) {}".format(fit.ship.item.name, fit.name)
|
||||
label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name)
|
||||
id = ContextMenu.nextID()
|
||||
self.fitMenuItemIds[id] = fit
|
||||
menuItem = wx.MenuItem(menu, id, label)
|
||||
@@ -67,7 +68,7 @@ class CommandFits(ContextMenu):
|
||||
if len(self.__class__.commandFits) < 15:
|
||||
for fit in sorted(self.__class__.commandFits, key=lambda x: x.name):
|
||||
menuItem = self.addFit(rootMenu if msw else sub, fit, True)
|
||||
sub.AppendItem(menuItem)
|
||||
sub.Append(menuItem)
|
||||
else:
|
||||
typeDict = {}
|
||||
|
||||
@@ -84,9 +85,9 @@ class CommandFits(ContextMenu):
|
||||
|
||||
for fit in sorted(typeDict[ship], key=lambda x: x.name):
|
||||
fitItem = self.addFit(rootMenu if msw else grandSub, fit, False)
|
||||
grandSub.AppendItem(fitItem)
|
||||
grandSub.Append(fitItem)
|
||||
|
||||
sub.AppendItem(shipItem)
|
||||
sub.Append(shipItem)
|
||||
|
||||
return sub
|
||||
|
||||
|
||||
@@ -3,15 +3,12 @@ import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.fit import Fit
|
||||
from service.damagePattern import DamagePattern as import_DamagePattern
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class DamagePattern(ContextMenu):
|
||||
@@ -52,7 +49,7 @@ class DamagePattern(ContextMenu):
|
||||
self.singles.append(pattern)
|
||||
|
||||
# return list of names, with singles first followed by submenu names
|
||||
self.m = map(lambda p: p.name, self.singles) + self.subMenus.keys()
|
||||
self.m = [p.name for p in self.singles] + list(self.subMenus.keys())
|
||||
return self.m
|
||||
|
||||
def addPattern(self, rootMenu, pattern):
|
||||
@@ -96,7 +93,7 @@ class DamagePattern(ContextMenu):
|
||||
|
||||
# Items that have a parent
|
||||
for pattern in self.subMenus[self.m[i]]:
|
||||
sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern))
|
||||
sub.Append(self.addPattern(rootMenu if msw else sub, pattern))
|
||||
|
||||
return sub
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class DroneSpinner(wx.Dialog):
|
||||
|
||||
bSizer1.Add(self.spinner, 1, wx.ALL, 5)
|
||||
|
||||
self.button = wx.Button(self, wx.ID_OK, u"Split")
|
||||
self.button = wx.Button(self, wx.ID_OK, "Split")
|
||||
bSizer1.Add(self.button, 0, wx.ALL, 5)
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
|
||||
@@ -3,7 +3,7 @@ import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
@@ -31,9 +31,7 @@ class FactorReload(ContextMenu):
|
||||
|
||||
def getBitmap(self, context, selection):
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit.factorReload:
|
||||
if sFit.serviceFittingOptions["useGlobalForceReload"]:
|
||||
return BitmapLoader.getBitmap("state_active_small", "gui")
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -44,7 +44,7 @@ class FighterAbility(ContextMenu):
|
||||
if not ability.effect.isImplemented:
|
||||
continue
|
||||
menuItem = self.addAbility(rootMenu if msw else sub, ability)
|
||||
sub.AppendItem(menuItem)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(ability.active)
|
||||
|
||||
return sub
|
||||
|
||||
@@ -61,7 +61,7 @@ class ImplantSets(ContextMenu):
|
||||
mitem = wx.MenuItem(rootMenu, id, set.name)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
|
||||
self.idmap[id] = set
|
||||
m.AppendItem(mitem)
|
||||
m.Append(mitem)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class ItemStats(ContextMenu):
|
||||
mstate = wx.GetMouseState()
|
||||
reuse = False
|
||||
|
||||
if mstate.CmdDown():
|
||||
if mstate.cmdDown:
|
||||
reuse = True
|
||||
|
||||
if self.mainFrame.GetActiveStatsWindow() is None and reuse:
|
||||
|
||||
@@ -91,7 +91,7 @@ class MetaSwap(ContextMenu):
|
||||
|
||||
# Sort items by metalevel, and group within that metalevel
|
||||
items = list(self.variations)
|
||||
print context
|
||||
print(context)
|
||||
if "implantItem" in context:
|
||||
# sort implants based on name
|
||||
items.sort(key=lambda x: x.name)
|
||||
@@ -116,14 +116,14 @@ class MetaSwap(ContextMenu):
|
||||
if thisgroup != group and context not in ("implantItem", "boosterItem"):
|
||||
group = thisgroup
|
||||
id = ContextMenu.nextID()
|
||||
m.Append(id, u'─ %s ─' % group)
|
||||
m.Append(id, '─ %s ─' % group)
|
||||
m.Enable(id, False)
|
||||
|
||||
id = ContextMenu.nextID()
|
||||
mitem = wx.MenuItem(rootMenu, id, item.name)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem)
|
||||
self.moduleLookup[id] = item
|
||||
m.AppendItem(mitem)
|
||||
m.Append(mitem)
|
||||
return m
|
||||
|
||||
def handleModule(self, event):
|
||||
|
||||
@@ -9,7 +9,7 @@ from eos.saveddata.module import Hardpoint
|
||||
import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
return False
|
||||
|
||||
self.modules = modules
|
||||
self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges))
|
||||
self.charges = list([charge for charge in validCharges if Market.getInstance().getPublicityByItem(charge)])
|
||||
return len(self.charges) > 0
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
@@ -58,9 +58,9 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
|
||||
def turretSorter(self, charge):
|
||||
damage = 0
|
||||
range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * \
|
||||
range_ = (self.module.getModifiedItemAttr("maxRange")) * \
|
||||
(charge.getAttribute("weaponRangeMultiplier") or 1)
|
||||
falloff = (self.module.getModifiedItemAttr("falloff") or 0) * \
|
||||
falloff = (self.module.getModifiedItemAttr("falloff")) * \
|
||||
(charge.getAttribute("fallofMultiplier") or 1)
|
||||
for type_ in self.DAMAGE_TYPES:
|
||||
d = charge.getAttribute("%sDamage" % type_)
|
||||
@@ -108,7 +108,7 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
|
||||
def nameSorter(self, charge):
|
||||
parts = charge.name.split(" ")
|
||||
return map(self.numericConverter, parts)
|
||||
return list(map(self.numericConverter, parts))
|
||||
|
||||
def addCharge(self, menu, charge):
|
||||
id_ = ContextMenu.nextID()
|
||||
@@ -127,7 +127,7 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
@staticmethod
|
||||
def addSeperator(m, text):
|
||||
id_ = ContextMenu.nextID()
|
||||
m.Append(id_, u'─ %s ─' % text)
|
||||
m.Append(id_, '─ %s ─' % text)
|
||||
m.Enable(id_, False)
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
@@ -137,7 +137,7 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
hardpoint = self.module.hardpoint
|
||||
moduleName = self.module.item.name
|
||||
# Make sure we do not consider mining turrets as combat turrets
|
||||
if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None:
|
||||
if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None:
|
||||
self.addSeperator(m, "Long Range")
|
||||
items = []
|
||||
range_ = None
|
||||
@@ -170,15 +170,15 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
self.addSeperator(sub, "Less Damage")
|
||||
item.SetSubMenu(sub)
|
||||
sub.AppendItem(self.addCharge(rootMenu if msw else sub, base))
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, base))
|
||||
|
||||
sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge))
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
||||
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
for item in items:
|
||||
m.AppendItem(item)
|
||||
m.Append(item)
|
||||
|
||||
self.addSeperator(m, "Short Range")
|
||||
elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher':
|
||||
@@ -203,23 +203,23 @@ class ModuleAmmoPicker(ContextMenu):
|
||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
self.addSeperator(sub, "Less Damage")
|
||||
item.SetSubMenu(sub)
|
||||
m.AppendItem(item)
|
||||
m.Append(item)
|
||||
|
||||
if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"):
|
||||
sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge))
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
||||
else:
|
||||
defender = charge
|
||||
|
||||
if defender is not None:
|
||||
m.AppendItem(self.addCharge(rootMenu if msw else m, defender))
|
||||
m.Append(self.addCharge(rootMenu if msw else m, defender))
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
else:
|
||||
self.charges.sort(key=self.nameSorter)
|
||||
for charge in self.charges:
|
||||
m.AppendItem(self.addCharge(rootMenu if msw else m, charge))
|
||||
m.Append(self.addCharge(rootMenu if msw else m, charge))
|
||||
|
||||
m.AppendItem(self.addCharge(rootMenu if msw else m, None))
|
||||
m.Append(self.addCharge(rootMenu if msw else m, None))
|
||||
return m
|
||||
|
||||
def handleAmmoSwitch(self, event):
|
||||
|
||||
@@ -35,7 +35,7 @@ class PriceOptions(ContextMenu):
|
||||
|
||||
for option in self.optionList:
|
||||
menuItem = self.addOption(rootMenu if msw else sub, option)
|
||||
sub.AppendItem(menuItem)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(self.settings.get(option.lower()))
|
||||
|
||||
return sub
|
||||
|
||||
@@ -38,15 +38,15 @@ class TabbedFits(ContextMenu):
|
||||
else:
|
||||
bindmenu = m
|
||||
|
||||
for page in self.mainFrame.fitMultiSwitch.pages:
|
||||
for page in self.mainFrame.fitMultiSwitch._pages:
|
||||
if isinstance(page, BlankPage):
|
||||
continue
|
||||
fit = sFit.getFit(page.activeFitID, basic=True)
|
||||
id = ContextMenu.nextID()
|
||||
mitem = wx.MenuItem(rootMenu, id, u"{}: {}".format(fit.ship.item.name, fit.name))
|
||||
mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name))
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
|
||||
self.fitLookup[id] = fit
|
||||
m.AppendItem(mitem)
|
||||
m.Append(mitem)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class TacticalMode(ContextMenu):
|
||||
|
||||
for mode in self.modes:
|
||||
menuItem = self.addMode(rootMenu if msw else sub, mode)
|
||||
sub.AppendItem(menuItem)
|
||||
sub.Append(menuItem)
|
||||
menuItem.Check(self.currMode.item == mode.item)
|
||||
|
||||
return sub
|
||||
|
||||
@@ -3,15 +3,11 @@ import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.targetResists import TargetResists as svc_TargetResists
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class TargetResists(ContextMenu):
|
||||
@@ -87,15 +83,15 @@ class TargetResists(ContextMenu):
|
||||
else:
|
||||
self.singles.append(pattern)
|
||||
|
||||
sub.AppendItem(self.addPattern(rootMenu if msw else sub, None)) # Add reset
|
||||
sub.Append(self.addPattern(rootMenu if msw else sub, None)) # Add reset
|
||||
sub.AppendSeparator()
|
||||
|
||||
# Single items, no parent
|
||||
for pattern in self.singles:
|
||||
sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern))
|
||||
sub.Append(self.addPattern(rootMenu if msw else sub, pattern))
|
||||
|
||||
# Items that have a parent
|
||||
for menuName, patterns in self.subMenus.items():
|
||||
for menuName, patterns in list(self.subMenus.items()):
|
||||
# Create parent item for root menu that is simply name of parent
|
||||
item = wx.MenuItem(rootMenu, ContextMenu.nextID(), menuName)
|
||||
|
||||
@@ -108,8 +104,8 @@ class TargetResists(ContextMenu):
|
||||
|
||||
# Append child items to child menu
|
||||
for pattern in patterns:
|
||||
grandSub.AppendItem(self.addPattern(rootMenu if msw else grandSub, pattern))
|
||||
sub.AppendItem(item) # finally, append parent item to root menu
|
||||
grandSub.Append(self.addPattern(rootMenu if msw else grandSub, pattern))
|
||||
sub.Append(item) # finally, append parent item to root menu
|
||||
|
||||
return sub
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class WhProjector(ContextMenu):
|
||||
subItem = wx.MenuItem(sub, wx.ID_ANY, swType)
|
||||
grandSub = wx.Menu()
|
||||
subItem.SetSubMenu(grandSub)
|
||||
sub.AppendItem(subItem)
|
||||
sub.Append(subItem)
|
||||
|
||||
for swData in sorted(effdata[swType], key=lambda tpl: tpl[2]):
|
||||
wxid = ContextMenu.nextID()
|
||||
@@ -45,7 +45,7 @@ class WhProjector(ContextMenu):
|
||||
rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
|
||||
else:
|
||||
grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
|
||||
grandSub.AppendItem(grandSubItem)
|
||||
grandSub.Append(grandSubItem)
|
||||
return sub
|
||||
|
||||
def handleSelection(self, event):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# =============================================================================
|
||||
|
||||
from gui.graph import Graph
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from eos.graph.fitDps import FitDpsGraph as FitDps
|
||||
from eos.graph import Data
|
||||
import gui.mainFrame
|
||||
@@ -54,7 +54,7 @@ class FitDpsGraph(Graph):
|
||||
def getIcons(self):
|
||||
icons = {}
|
||||
sAttr = Attribute.getInstance()
|
||||
for key, attrName in self.propertyAttributeMap.iteritems():
|
||||
for key, attrName in self.propertyAttributeMap.items():
|
||||
iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
if bitmap:
|
||||
@@ -69,7 +69,7 @@ class FitDpsGraph(Graph):
|
||||
|
||||
fitDps.clearData()
|
||||
variable = None
|
||||
for fieldName, value in fields.iteritems():
|
||||
for fieldName, value in fields.items():
|
||||
d = Data(fieldName, value)
|
||||
if not d.isConstant():
|
||||
if variable is None:
|
||||
|
||||
@@ -14,7 +14,7 @@ from eos.saveddata.fit import Fit
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
|
||||
|
||||
class ItemAffectedBy(wx.Panel):
|
||||
@@ -43,17 +43,17 @@ class ItemAffectedBy(wx.Panel):
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, "Expand All", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle View", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
if stuff is not None:
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree)
|
||||
|
||||
@@ -74,9 +74,9 @@ class ItemAffectedBy(wx.Panel):
|
||||
def spawnMenu(self, item):
|
||||
self.affectedBy.SelectItem(item)
|
||||
|
||||
stuff = self.affectedBy.GetPyData(item)
|
||||
stuff = self.affectedBy.GetItemData(item)
|
||||
# String is set as data when we are dealing with attributes, not stuff containers
|
||||
if stuff is None or isinstance(stuff, basestring):
|
||||
if stuff is None or isinstance(stuff, str):
|
||||
return
|
||||
contexts = []
|
||||
|
||||
@@ -109,10 +109,10 @@ class ItemAffectedBy(wx.Panel):
|
||||
self.Freeze()
|
||||
|
||||
for item in self.treeItems:
|
||||
change = self.affectedBy.GetPyData(item)
|
||||
change = self.affectedBy.GetItemData(item)
|
||||
display = self.affectedBy.GetItemText(item)
|
||||
self.affectedBy.SetItemText(item, change)
|
||||
self.affectedBy.SetPyData(item, display)
|
||||
self.affectedBy.SetItemData(item, display)
|
||||
|
||||
self.Thaw()
|
||||
|
||||
@@ -141,7 +141,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
# sheri was here
|
||||
del self.treeItems[:]
|
||||
root = self.affectedBy.AddRoot("WINPWNZ0R")
|
||||
self.affectedBy.SetPyData(root, None)
|
||||
self.affectedBy.SetItemData(root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.affectedBy.SetImageList(self.imageList)
|
||||
@@ -185,7 +185,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
if attributes[attrName] == (attributes.getOriginal(attrName, 0)):
|
||||
continue
|
||||
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).iteritems():
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).items():
|
||||
for afflictor, modifier, amount, used in afflictors:
|
||||
|
||||
if not used or afflictor.item is None:
|
||||
@@ -216,7 +216,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
|
||||
|
||||
# Make sure projected fits are on top
|
||||
rootOrder = container.keys()
|
||||
rootOrder = list(container.keys())
|
||||
rootOrder.sort(key=lambda x: self.ORDER.index(type(x)))
|
||||
|
||||
# Now, we take our created dictionary and start adding stuff to our tree
|
||||
@@ -230,7 +230,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
parent = child
|
||||
|
||||
attributes = container[thing]
|
||||
attrOrder = sorted(attributes.keys(), key=self.sortAttrDisplayName)
|
||||
attrOrder = sorted(list(attributes.keys()), key=self.sortAttrDisplayName)
|
||||
|
||||
for attrName in attrOrder:
|
||||
attrInfo = self.stuff.item.attributes.get(attrName)
|
||||
@@ -257,7 +257,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
|
||||
# this is the attribute node
|
||||
child = self.affectedBy.AppendItem(parent, display, attrIcon)
|
||||
self.affectedBy.SetPyData(child, saved)
|
||||
self.affectedBy.SetItemData(child, saved)
|
||||
self.treeItems.append(child)
|
||||
|
||||
items = attributes[attrName]
|
||||
@@ -284,12 +284,12 @@ class ItemAffectedBy(wx.Panel):
|
||||
penalized += "(penalized)"
|
||||
if 'r' in attrModifier:
|
||||
penalized += "(resisted)"
|
||||
attrModifier = "*"
|
||||
attrModifier = "*"
|
||||
|
||||
# this is the Module node, the attribute will be attached to this
|
||||
display = "%s %s %.2f %s" % (displayStr, attrModifier, attrAmount, penalized)
|
||||
treeItem = self.affectedBy.AppendItem(child, display, itemIcon)
|
||||
self.affectedBy.SetPyData(treeItem, afflictor)
|
||||
self.affectedBy.SetItemData(treeItem, afflictor)
|
||||
|
||||
def buildModuleView(self, root):
|
||||
"""
|
||||
@@ -314,7 +314,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
if attributes[attrName] == (attributes.getOriginal(attrName, 0)):
|
||||
continue
|
||||
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).iteritems():
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).items():
|
||||
for afflictor, modifier, amount, used in afflictors:
|
||||
if not used or getattr(afflictor, 'item', None) is None:
|
||||
continue
|
||||
@@ -350,7 +350,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
info[2].append((attrName, modifier, amount))
|
||||
|
||||
# Make sure projected fits are on top
|
||||
rootOrder = container.keys()
|
||||
rootOrder = list(container.keys())
|
||||
rootOrder.sort(key=lambda x: self.ORDER.index(type(x)))
|
||||
|
||||
# Now, we take our created dictionary and start adding stuff to our tree
|
||||
@@ -364,7 +364,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
parent = child
|
||||
|
||||
items = container[thing]
|
||||
order = items.keys()
|
||||
order = list(items.keys())
|
||||
order.sort(key=lambda x: (self.ORDER.index(items[x][0]), x))
|
||||
|
||||
for itemName in order:
|
||||
@@ -389,7 +389,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
|
||||
# this is the Module node, the attribute will be attached to this
|
||||
child = self.affectedBy.AppendItem(parent, displayStr, itemIcon)
|
||||
self.affectedBy.SetPyData(child, afflictors.pop())
|
||||
self.affectedBy.SetItemData(child, afflictors.pop())
|
||||
|
||||
if counter > 0:
|
||||
attributes = []
|
||||
@@ -416,7 +416,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
penalized += "(penalized)"
|
||||
if 'r' in attrModifier:
|
||||
penalized += "(resisted)"
|
||||
attrModifier = "*"
|
||||
attrModifier = "*"
|
||||
|
||||
attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier,
|
||||
attrAmount, penalized, attrIcon))
|
||||
@@ -443,5 +443,5 @@ class ItemAffectedBy(wx.Panel):
|
||||
saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized)
|
||||
|
||||
treeitem = self.affectedBy.AppendItem(child, display, attrIcon)
|
||||
self.affectedBy.SetPyData(treeitem, saved)
|
||||
self.affectedBy.SetItemData(treeitem, saved)
|
||||
self.treeItems.append(treeitem)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import sys
|
||||
import csv
|
||||
import config
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
from .helpers import AutoListCtrl
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.market import Market
|
||||
from service.attribute import Attribute
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
@@ -34,19 +33,19 @@ class ItemParams(wx.Panel):
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize,
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize,
|
||||
self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, "Export Item Stats", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
if stuff is not None:
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues)
|
||||
|
||||
@@ -166,7 +165,7 @@ class ItemParams(wx.Panel):
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL)
|
||||
|
||||
names = list(self.attrValues.iterkeys())
|
||||
names = list(self.attrValues.keys())
|
||||
names.sort()
|
||||
|
||||
idNameMap = {}
|
||||
@@ -203,7 +202,7 @@ class ItemParams(wx.Panel):
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
|
||||
|
||||
index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon)
|
||||
index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon)
|
||||
idNameMap[idCount] = attrName
|
||||
self.paramList.SetItemData(index, idCount)
|
||||
idCount += 1
|
||||
@@ -222,11 +221,12 @@ class ItemParams(wx.Panel):
|
||||
else:
|
||||
valueUnitDefault = formatAmount(valueDefault, 3, 0, 0)
|
||||
|
||||
self.paramList.SetStringItem(index, 1, valueUnit)
|
||||
self.paramList.SetItem(index, 1, valueUnit)
|
||||
if self.stuff is not None:
|
||||
self.paramList.SetStringItem(index, 2, valueUnitDefault)
|
||||
|
||||
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
|
||||
self.paramList.SetItem(index, 2, valueUnitDefault)
|
||||
# @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the
|
||||
# long run, take a look
|
||||
self.paramList.SortItems(lambda id1, id2: (idNameMap[id1] > idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2]))
|
||||
self.paramList.RefreshRows()
|
||||
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
|
||||
self.Layout()
|
||||
@@ -252,7 +252,7 @@ class ItemParams(wx.Panel):
|
||||
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
|
||||
"Modifier Percent" : (
|
||||
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
|
||||
"Volume" : (lambda: value, u"m\u00B3"),
|
||||
"Volume" : (lambda: value, "m\u00B3"),
|
||||
"Sizeclass" : (lambda: value, ""),
|
||||
"Absolute Percent" : (lambda: (value * 100), unitName),
|
||||
"Milliseconds" : (lambda: value / 1000.0, unitName),
|
||||
@@ -266,7 +266,7 @@ class ItemParams(wx.Panel):
|
||||
v = override[0]()
|
||||
if isinstance(v, str):
|
||||
fvalue = v
|
||||
elif isinstance(v, (int, float, long)):
|
||||
elif isinstance(v, (int, float)):
|
||||
fvalue = formatAmount(v, 3, 0, 0)
|
||||
else:
|
||||
fvalue = v
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import sys
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
from .helpers import AutoListCtrl
|
||||
from service.price import Price as ServicePrice
|
||||
from service.market import Market
|
||||
from service.attribute import Attribute
|
||||
@@ -30,17 +28,17 @@ class ItemCompare(wx.Panel):
|
||||
self.sortReverse = False
|
||||
self.item = item
|
||||
self.items = sorted(items,
|
||||
key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None)
|
||||
key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else 0)
|
||||
self.attrs = {}
|
||||
|
||||
# get a dict of attrName: attrInfo of all unique attributes across all items
|
||||
for item in self.items:
|
||||
for attr in item.attributes.keys():
|
||||
for attr in list(item.attributes.keys()):
|
||||
if item.attributes[attr].info.displayName:
|
||||
self.attrs[attr] = item.attributes[attr].info
|
||||
|
||||
# Process attributes for items and find ones that differ
|
||||
for attr in self.attrs.keys():
|
||||
for attr in list(self.attrs.keys()):
|
||||
value = None
|
||||
|
||||
for item in self.items:
|
||||
@@ -65,14 +63,14 @@ class ItemCompare(wx.Panel):
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition,
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition,
|
||||
wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize,
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues)
|
||||
@@ -109,7 +107,7 @@ class ItemCompare(wx.Panel):
|
||||
|
||||
def processPrices(self, prices):
|
||||
for i, price in enumerate(prices):
|
||||
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True))
|
||||
self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True))
|
||||
|
||||
def PopulateList(self, sort=None):
|
||||
|
||||
@@ -126,7 +124,7 @@ class ItemCompare(wx.Panel):
|
||||
try:
|
||||
# Remember to reduce by 1, because the attrs array
|
||||
# starts at 0 while the list has the item name as column 0.
|
||||
attr = str(self.attrs.keys()[sort - 1])
|
||||
attr = str(list(self.attrs.keys())[sort - 1])
|
||||
func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None
|
||||
except IndexError:
|
||||
# Clicked on a column that's not part of our array (price most likely)
|
||||
@@ -147,7 +145,7 @@ class ItemCompare(wx.Panel):
|
||||
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
|
||||
|
||||
for item in self.items:
|
||||
i = self.paramList.InsertStringItem(sys.maxint, item.name)
|
||||
i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name)
|
||||
for x, attr in enumerate(self.attrs.keys()):
|
||||
if attr in item.attributes:
|
||||
info = self.attrs[attr]
|
||||
@@ -159,10 +157,10 @@ class ItemCompare(wx.Panel):
|
||||
else:
|
||||
valueUnit = formatAmount(value, 3, 0, 0)
|
||||
|
||||
self.paramList.SetStringItem(i, x + 1, valueUnit)
|
||||
self.paramList.SetItem(i, x + 1, valueUnit)
|
||||
|
||||
# Add prices
|
||||
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True))
|
||||
self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True))
|
||||
|
||||
self.paramList.RefreshRows()
|
||||
self.Layout()
|
||||
@@ -185,7 +183,7 @@ class ItemCompare(wx.Panel):
|
||||
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
|
||||
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
|
||||
"Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
|
||||
"Volume" : (lambda: value, u"m\u00B3"),
|
||||
"Volume" : (lambda: value, "m\u00B3"),
|
||||
"Sizeclass" : (lambda: value, ""),
|
||||
"Absolute Percent" : (lambda: (value * 100), unitName),
|
||||
"Milliseconds" : (lambda: value / 1000.0, unitName),
|
||||
@@ -199,7 +197,7 @@ class ItemCompare(wx.Panel):
|
||||
v = override[0]()
|
||||
if isinstance(v, str):
|
||||
fvalue = v
|
||||
elif isinstance(v, (int, float, long)):
|
||||
elif isinstance(v, (int, float)):
|
||||
fvalue = formatAmount(v, 3, 0, 0)
|
||||
else:
|
||||
fvalue = v
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
|
||||
|
||||
class ItemDependents(wx.Panel):
|
||||
@@ -19,7 +19,7 @@ class ItemDependents(wx.Panel):
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.root = self.reqTree.AddRoot("WINRARZOR")
|
||||
self.reqTree.SetPyData(self.root, None)
|
||||
self.reqTree.SetItemData(self.root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.reqTree.SetImageList(self.imageList)
|
||||
@@ -32,7 +32,7 @@ class ItemDependents(wx.Panel):
|
||||
def getFullSkillTree(self, parentSkill, parent, sbIconId):
|
||||
levelToItems = {}
|
||||
|
||||
for item, level in parentSkill.requiredFor.iteritems():
|
||||
for item, level in parentSkill.requiredFor.items():
|
||||
if level not in levelToItems:
|
||||
levelToItems[level] = []
|
||||
levelToItems[level].append(item)
|
||||
|
||||
@@ -11,8 +11,8 @@ class ItemDescription(wx.Panel):
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
bgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
|
||||
fgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
|
||||
bgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
|
||||
fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
|
||||
|
||||
self.description = wx.html.HtmlWindow(self)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import config
|
||||
@@ -6,7 +5,7 @@ import config
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
from .helpers import AutoListCtrl
|
||||
|
||||
|
||||
class ItemEffects(wx.Panel):
|
||||
@@ -47,11 +46,11 @@ class ItemEffects(wx.Panel):
|
||||
|
||||
item = self.item
|
||||
effects = item.effects
|
||||
names = list(effects.iterkeys())
|
||||
names = list(effects.keys())
|
||||
names.sort()
|
||||
|
||||
for name in names:
|
||||
index = self.effectList.InsertStringItem(sys.maxint, name)
|
||||
index = self.effectList.InsertItem(self.effectList.GetItemCount(), name)
|
||||
|
||||
if effects[name].isImplemented:
|
||||
if effects[name].activeByDefault:
|
||||
@@ -72,11 +71,11 @@ class ItemEffects(wx.Panel):
|
||||
else:
|
||||
effectRunTime = ""
|
||||
|
||||
self.effectList.SetStringItem(index, 1, activeByDefault)
|
||||
self.effectList.SetStringItem(index, 2, effectTypeText)
|
||||
self.effectList.SetItem(index, 1, activeByDefault)
|
||||
self.effectList.SetItem(index, 2, effectTypeText)
|
||||
if config.debug:
|
||||
self.effectList.SetStringItem(index, 3, effectRunTime)
|
||||
self.effectList.SetStringItem(index, 4, str(effects[name].ID))
|
||||
self.effectList.SetItem(index, 3, effectRunTime)
|
||||
self.effectList.SetItem(index, 4, str(effects[name].ID))
|
||||
|
||||
self.effectList.RefreshRows()
|
||||
self.Layout()
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import sys
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
from .helpers import AutoListCtrl
|
||||
|
||||
|
||||
class ItemProperties(wx.Panel):
|
||||
@@ -27,7 +25,7 @@ class ItemProperties(wx.Panel):
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
|
||||
@@ -79,7 +77,7 @@ class ItemProperties(wx.Panel):
|
||||
attrName = name.title()
|
||||
value = getattr(self.item, name)
|
||||
|
||||
index = self.paramList.InsertStringItem(sys.maxint, attrName)
|
||||
index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName)
|
||||
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
|
||||
idNameMap[idCount] = attrName
|
||||
self.paramList.SetItemData(index, idCount)
|
||||
@@ -87,13 +85,13 @@ class ItemProperties(wx.Panel):
|
||||
|
||||
valueUnit = str(value)
|
||||
|
||||
self.paramList.SetStringItem(index, 1, valueUnit)
|
||||
self.paramList.SetItem(index, 1, valueUnit)
|
||||
except:
|
||||
# TODO: Add logging to this.
|
||||
# We couldn't get a property for some reason. Skip it for now.
|
||||
continue
|
||||
|
||||
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
|
||||
self.paramList.SortItems(lambda id1, id2: (idNameMap[id1] > idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2]))
|
||||
self.paramList.RefreshRows()
|
||||
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
|
||||
self.Layout()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
|
||||
|
||||
class ItemRequirements(wx.Panel):
|
||||
@@ -19,7 +19,7 @@ class ItemRequirements(wx.Panel):
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.root = self.reqTree.AddRoot("WINRARZOR")
|
||||
self.reqTree.SetPyData(self.root, None)
|
||||
self.reqTree.SetItemData(self.root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.reqTree.SetImageList(self.imageList)
|
||||
@@ -32,7 +32,7 @@ class ItemRequirements(wx.Panel):
|
||||
self.Layout()
|
||||
|
||||
def getFullSkillTree(self, parentSkill, parent, sbIconId):
|
||||
for skill, level in parentSkill.requiredSkills.iteritems():
|
||||
for skill, level in parentSkill.requiredSkills.items():
|
||||
child = self.reqTree.AppendItem(parent, "%s %s" % (skill.name, self.romanNb[int(level)]), sbIconId)
|
||||
if skill.ID not in self.skillIdHistory:
|
||||
self.getFullSkillTree(skill, child, sbIconId)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user