diff --git a/.appveyor.yml b/.appveyor.yml index e63978a5f..821bbe7d3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,4 @@ environment: - global: # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the # /E:ON and /V:ON options are not enabled in the batch script intepreter @@ -8,76 +7,11 @@ environment: matrix: - # Python 2.7.10 is the latest version and is not pre-installed. - - - PYTHON: "C:\\Python27.10" - PYTHON_VERSION: "2.7.10" + - PYTHON: "C:\\Python36" + PYTHON_VERSION: "3.6.x" PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python27.10-x64" - # PYTHON_VERSION: "2.7.10" - # PYTHON_ARCH: "64" - - # Pre-installed Python versions, which Appveyor may upgrade to - # a later point release. - # See: http://www.appveyor.com/docs/installed-software#python - - #- PYTHON: "C:\\Python27" - # PYTHON_VERSION: "2.7.x" # currently 2.7.9 - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python27-x64" - # PYTHON_VERSION: "2.7.x" # currently 2.7.9 - # PYTHON_ARCH: "64" - - #- PYTHON: "C:\\Python33" - # PYTHON_VERSION: "3.3.x" # currently 3.3.5 - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python33-x64" - # PYTHON_VERSION: "3.3.x" # currently 3.3.5 - # PYTHON_ARCH: "64" - - #- PYTHON: "C:\\Python34" - # PYTHON_VERSION: "3.4.x" # currently 3.4.3 - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python34-x64" - # PYTHON_VERSION: "3.4.x" # currently 3.4.3 - # PYTHON_ARCH: "64" - - # Python versions not pre-installed - - # Python 2.6.6 is the latest Python 2.6 with a Windows installer - # See: https://github.com/ogrisel/python-appveyor-demo/issues/10 - - #- PYTHON: "C:\\Python266" - # PYTHON_VERSION: "2.6.6" - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python266-x64" - # PYTHON_VERSION: "2.6.6" - # PYTHON_ARCH: "64" - - #- PYTHON: "C:\\Python35" - # PYTHON_VERSION: "3.5.0" - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python35-x64" - # PYTHON_VERSION: "3.5.0" - # PYTHON_ARCH: "64" - - # Major and minor releases (i.e x.0.0 and x.y.0) prior to 3.3.0 use - # a different naming scheme. - - #- PYTHON: "C:\\Python270" - # PYTHON_VERSION: "2.7.0" - # PYTHON_ARCH: "32" - - #- PYTHON: "C:\\Python270-x64" - # PYTHON_VERSION: "2.7.0" - # PYTHON_ARCH: "64" - +init: + - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: # If there is a newer build queued for the same PR, cancel this one. # The AppVeyor 'rollout builds' option is supposed to serve the same @@ -89,34 +23,23 @@ install: Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - # Install wxPython - - 'ECHO Downloading wxPython.' - - "appveyor DownloadFile https://goo.gl/yvO8PB -FileName C:\\wxpython.exe" - #- "appveyor DownloadFile https://goo.gl/Uj0jV3 -FileName C:\\wxpython64.exe" - - - 'ECHO Install wxPython' - - "C:\\wxpython.exe /SP- /VERYSILENT /NORESTART" - #- "C:\\wxpython64.exe /SP- /VERYSILENT /NORESTART" - - ECHO "Filesystem root:" - ps: "ls \"C:/\"" + - ECHO "Filesystem projects root:" + - ps: "ls \"C:\\projects\\\"" + - ECHO "Filesystem pyfa root:" - - ps: "ls \"C:\\projects\\pyfa\\\"" + - ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\"" - ECHO "Installed SDKs:" - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" - # Install Python (from the official .msi of http://python.org) and pip when - # not already installed. - # - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 } - # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - # Check that we have the expected version and architecture for Python - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" @@ -128,19 +51,36 @@ install: # compiled extensions and are not provided as pre-built wheel packages, # pip will build them from source using the MSVC compiler matching the # target Python version and architecture - # C:\\projects\\eve-gnosis\\ - ECHO "Install pip requirements:" - "pip install -r requirements.txt" - - "pip install -r requirements_test.txt" - - "pip install -r requirements_build_windows.txt" + - "pip install PyInstaller" + +before_build: + # directory that will contain the built files + - ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist" + - ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py) + - ps: echo("pyfa version ") + - ps: echo ($env:PYFA_VERSION) build_script: - # Build the compiled extension - # - "python setup.py build" - ECHO "Build pyfa:" - #- copy C:\projects\pyfa\dist_assets\win\pyfa.spec C:\projects\pyfa\pyfa.spec - - "python C:\\projects\\pyfa\\setup.py build" + ########## + # PyInstaller - create binaries for pyfa + ########## + # Build command for PyInstaller + - "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec" + # Copy over manifest (See pyfa-org/pyfa#1622) + - ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\ + # Not really sure if this is needed, but why not + - ps: xcopy /y dist_assets\win\Microsoft.VC90.CRT.manifest $env:PYFA_DIST_DIR\pyfa\ + + ########## + # InnoScript EXE building + # This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now... + ########## + - "python dist_assets/win/dist.py" + - ps: dir $env:PYFA_DIST_DIR/ #- ECHO "Build pyfa (Debug):" #- copy C:\projects\pyfa\dist_assets\win\pyfa_debug.spec C:\projects\pyfa\pyfa_debug.spec #- "pyinstaller.exe --clean --noconfirm --windowed --upx-dir=C:\\projects\\pyfa\\scripts\\upx.exe C:\\projects\\pyfa\\pyfa_debug.spec" @@ -150,12 +90,11 @@ build: on after_build: - ps: "ls \"./\"" #- ps: "ls \"C:\\projects\\pyfa\\build\\pyfa\\\"" - - ps: "ls \"C:\\projects\\pyfa\\build\\\"" - - ps: "ls \"C:\\projects\\pyfa\\build\\exe.win32-2.7\\\"" +# - ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\build\\exe.win32-2.7\\\"" # Zip # APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER #- 7z a build.zip -r C:\projects\pyfa\build\pyfa\*.* - - 7z a pyfa.zip -r C:\projects\pyfa\build\exe.win32-2.7\*.* + - ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*.*" #- 7z a pyfa_debug.zip -r C:\projects\pyfa\dist\pyfa_debug\*.* on_success: @@ -176,11 +115,21 @@ after_test: artifacts: # Archive the generated packages in the ci.appveyor.com build report. - - path: pyfa.zip - name: 'pyfa.zip' + - path: pyfa*-win.zip + - path: pyfa*-win.exe #- path: pyfa_debug.zip # name: Pyfa_debug - + +deploy: + tag: $(pyfa_version) + release: pyfa $(pyfa_version) + description: 'Release description' + provider: GitHub + auth_token: + secure: BfNHO66ff5hVx2O2ORbl49X0U/5h2V2T0IuRZDwm7fd1HvsVluF0wRCbl29oRp1M + draft: true + on: + APPVEYOR_REPO_TAG: true # deploy on tag push only #on_success: # - TODO: upload the content of dist/*.whl to a public wheelhouse # \ No newline at end of file diff --git a/.gitignore b/.gitignore index c5efd554c..a9eb5e25c 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ gitversion /.version *.swp +*.fsdbinary diff --git a/.travis.yml b/.travis.yml index 2b4dbf181..9140c2f10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,39 +1,29 @@ +os: linux language: python -cache: pip python: - - '2.7' -env: - - TOXENV=pep8 -addons: - apt: - packages: + - 3.6 +matrix: + include: + - os: osx + osx_image: xcode7.3 + language: generic + env: PYTHON=3.6.1 before_install: - - sudo apt-get update && sudo apt-get --reinstall install -qq language-pack-en language-pack-ru language-pack-he language-pack-zh-hans - - pip install tox - # We're not actually installing Tox, but have to run it before we install wxPython via Conda. This is fugly but vOv - - tox - # get Conda - - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; - else - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; - fi - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - - conda config --set always_yes yes --set changeps1 no - - conda update -q conda - # Useful for debugging any issues with conda - - conda info -a + - bash scripts/setup-osx.sh install: - # install wxPython 3.0.0.0 - - conda install -c https://conda.anaconda.org/travis wxpython -before_script: - - pip install -r requirements.txt - - pip install -r requirements_test.txt -script: - - py.test --cov=./ -after_success: - - bash <(curl -s https://codecov.io/bash) + - export PYFA_VERSION="$(python3 scripts/dump_version.py)" + - bash scripts/package-osx.sh before_deploy: - - pip install -r requirements_build_linux.txt + - export RELEASE_PKG_FILE=$(ls *.deb) + - echo "deploying $RELEASE_PKG_FILE to GitHub releases" +deploy: + provider: releases + api_key: + secure: Xfu0xApoB0zUPLXl29aYUulVC3iA4/3bXQwwADKCfAKZwxgNon4dLbO7Rie5/7Ukf2POL0KwmRaQGN3kOr+XSoIVTE4M5sXxnhiaaLGKQ+48hDizLE6JuXcZGJvkxUaghaTzIdCwHsG7VGBsPfQgfGsjJcfBp8tFNLmRyM/Jpsr8T6BR2MxtBIEUVy8zrOWFNZqnmWrY2pWMsB9fYt3JFNdpqeIgRAYqbBsBcZQ1MngLTi3ztuYS5IaF+lk06RrnBlHmUsJu/5nCvIpvPvD0i2BLZ3Uu0+Fn+8QWUgjJEL9MNseXZMXynu05xd8YRk7Ajc9CUrzQIIbAktyteYp85kE3pUJHmrMLcXhh7nqkwttR5/47Zwa3OLJLJFKBxMx6wY5jFkJjkV08850B7aWrmTFl/Eqc3Q5nZMuiEt3wFRbjxHi9h1mTN/fkxfRRHg8u3ENGPR+ZPiFC3J18qtks/B/hsKjjHvZP1i79OYlET4V/zyLyyQkCbpDaARQANuotLYJyZ7tH+KWEyRsvTi0M9Yev9mNNw6aI4vzh4HfkEhvcvnWnYwckPj1dnjQ573Qpw0Z9wsconoWfHAn+hBDt3+YLMrrFZl++mCRskHH1mZChX3aGMDi49zD0kfxBUkYPOAhguc6PwudBxHUZP+O6T/SoHylff6EizCE/k5dGeAk= + file_glob: true + file: "dist/pyfa-*.zip" + skip_cleanup: true + draft: true + on: + tags: true + repo: pyfa-org/Pyfa diff --git a/_development/helpers.py b/_development/helpers.py index 8af8f9733..ad1447371 100644 --- a/_development/helpers.py +++ b/_development/helpers.py @@ -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, diff --git a/_development/helpers_fits.py b/_development/helpers_fits.py index a0506827d..347b0622b 100644 --- a/_development/helpers_fits.py +++ b/_development/helpers_fits.py @@ -1,7 +1,6 @@ import pytest # noinspection PyPackageRequirements -from _development.helpers import DBInMemory as DB, Gamedata, Saveddata # noinspection PyShadowingNames diff --git a/_development/helpers_items.py b/_development/helpers_items.py index 7ae722e23..d77adb026 100644 --- a/_development/helpers_items.py +++ b/_development/helpers_items.py @@ -1,7 +1,6 @@ import pytest # noinspection PyPackageRequirements -from _development.helpers import DBInMemory as DB, Gamedata, Saveddata # noinspection PyShadowingNames diff --git a/config.py b/config.py index 5da7854bc..4e1234e90 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,6 @@ import os import sys +import yaml from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \ StreamHandler, TimedRotatingFileHandler, WARNING @@ -22,12 +23,6 @@ debug = False # Defines if our saveddata will be in pyfa root or not saveInRoot = False -# Version data - -version = "2.0.0" -tag = "Stable" -expansionName = "YC120.3" -expansionVersion = "1.8" evemonMinVersion = "4081" minItemSearchLength = 3 @@ -42,7 +37,6 @@ logging_setup = None cipher = None clientHash = None -ESI_AUTH_PROXY = "https://www.pyfa.io" # "http://localhost:5015" ESI_CACHE = 'esi_cache' LOGLEVEL_MAP = { @@ -80,12 +74,7 @@ def getPyfaRoot(): 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)" + return version def getDefaultSave(): @@ -97,11 +86,12 @@ def defPaths(customSavePath=None): global pyfaPath global savePath global saveDB - global gameDB + global gameDB global saveInRoot global logPath global cipher global clientHash + global version pyfalog.debug("Configuring Pyfa") @@ -111,6 +101,12 @@ def defPaths(customSavePath=None): if pyfaPath is None: pyfaPath = getPyfaRoot() + # Version data + + with open(os.path.join(pyfaPath, "version.yml"), 'r') as file: + data = yaml.load(file) + version = data['version'] + # Where we store the saved fits etc, default is the current users home directory if saveInRoot is True: savePath = getattr(configforced, "savePath", None) @@ -230,20 +226,6 @@ def defLogging(): ) ]) - 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): diff --git a/dist_assets/mac/pyfa.spec b/dist_assets/mac/pyfa.spec index a3214b3d3..1e1f35711 100644 --- a/dist_assets/mac/pyfa.spec +++ b/dist_assets/mac/pyfa.spec @@ -24,11 +24,15 @@ added_files = [ ('../../eve.db', '.'), ('../../README.md', '.'), ('../../LICENSE', '.'), - ('../../.version', '.'), + ('../../version.yml', '.'), ] -import_these = [] +import_these = [ + 'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982 +] + +icon = os.path.join(os.getcwd(), "dist_assets", "mac", "pyfa.icns") # Walk directories that do dynamic importing paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') @@ -52,8 +56,10 @@ a = Analysis([r'../../pyfa.py'], 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, @@ -65,10 +71,19 @@ exe = EXE(pyz, upx=True, runtime_tmpdir=None, console=False , - icon='dist_assets/mac/pyfa.icns', + icon=icon, ) -app = BUNDLE(exe, - name='pyfa.app', - icon=None, - bundle_identifier=None) \ No newline at end of file +app = BUNDLE( + exe, + name='pyfa.app', + icon=icon, + bundle_identifier=None, + info_plist={ + 'NSHighResolutionCapable': 'True', + 'NSPrincipalClass': 'NSApplication', + 'CFBundleName': 'pyfa', + 'CFBundleDisplayName': 'pyfa', + 'CFBundleIdentifier': 'org.pyfaorg.pyfa', + } +) \ No newline at end of file diff --git a/dist_assets/win/Microsoft.VC90.CRT.manifest b/dist_assets/win/Microsoft.VC90.CRT.manifest new file mode 100644 index 000000000..627da999b --- /dev/null +++ b/dist_assets/win/Microsoft.VC90.CRT.manifest @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/dist_assets/win/dist.py b/dist_assets/win/dist.py new file mode 100644 index 000000000..bff55018a --- /dev/null +++ b/dist_assets/win/dist.py @@ -0,0 +1,37 @@ +# helper script to zip up pyinstaller distribution and create installer file + +import os.path +from subprocess import call +import zipfile +from packaging.version import Version +import yaml + + +with open("version.yml", 'r') as file: + data = yaml.load(file) + version = data['version'] + +os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist') + +os.environ["PYFA_VERSION"] = version +iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine + +source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa") + +fileName = "pyfa-{}-win".format(os.environ["PYFA_VERSION"]) + +print("Compiling EXE") + +v = Version(version) + +print(v) + +call([ + iscc, + os.path.join(os.getcwd(), "dist_assets", "win", "pyfa-setup.iss"), + "/dMyAppVersion=%s" % v, + "/dMyAppDir=%s" % source, + "/dMyOutputDir=%s" % os.path.join(os.getcwd()), + "/dMyOutputFile=%s" % fileName]) # stdout=devnull, stderr=devnull + +print("Done") diff --git a/scripts/pyfa-setup.iss b/dist_assets/win/pyfa-setup.iss similarity index 81% rename from scripts/pyfa-setup.iss rename to dist_assets/win/pyfa-setup.iss index 9c25f11f2..e016b3a37 100644 --- a/scripts/pyfa-setup.iss +++ b/dist_assets/win/pyfa-setup.iss @@ -5,24 +5,22 @@ ; we do some #ifdef conditionals because automated compilation passes these as arguments #ifndef MyAppVersion - #define MyAppVersion "1.15.0" -#endif -#ifndef MyAppExpansion - #define MyAppExpansion "Vanguard 1.0" + #define MyAppVersion "2.1.0" #endif ; Other config #define MyAppName "pyfa" #define MyAppPublisher "pyfa" -#define MyAppURL "https://forums.eveonline.com/t/27156" +#define MyAppURL "https://github.com/pyfa-org/Pyfa/" #define MyAppExeName "pyfa.exe" ; What version starts with the new structure (1.x.0). This is used to determine if we run directory structure cleanup -#define VersionFlag 16 +#define MajorVersionFlag 2 +#define MinorVersionFlag 0 #ifndef MyOutputFile - #define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-'+MyAppExpansion+'-win-wx3', " ", "-")) + #define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-win', " ", "-")) #endif #ifndef MyAppDir #define MyAppDir "pyfa" @@ -38,7 +36,7 @@ ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{3DA39096-C08D-49CD-90E0-1D177F32C8AA} AppName={#MyAppName} -AppVersion={#MyAppVersion} ({#MyAppExpansion}) +AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} @@ -50,10 +48,8 @@ LicenseFile={#MyAppDir}\LICENSE OutputDir={#MyOutputDir} OutputBaseFilename={#MyOutputFile} SetupIconFile={#MyAppDir}\pyfa.ico -Compression=lzma SolidCompression=yes CloseApplications=yes -AppReadmeFile=https://github.com/pyfa-org/Pyfa/blob/v{#MyAppVersion}/readme.txt [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" @@ -63,7 +59,7 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1 [Files] -Source: "{#MyAppDir}\pyfa.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "{#MyAppDir}\pyfa.exe"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall: RemoveFromVirtualStore Source: "{#MyAppDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files @@ -103,6 +99,22 @@ begin FSWbemLocator := Unassigned; end; +procedure RemoveFromVirtualStore; +var + VirtualStore,FileName,FilePath:String; + DriveChars:Integer; +begin + VirtualStore:=AddBackslash(ExpandConstant('{localappdata}'))+'VirtualStore'; + FileName:=ExpandConstant(CurrentFileName); + DriveChars:=Length(ExtractFileDrive(FileName)); + if DriveChars>0 then begin + Delete(FileName,1,DriveChars); + FileName:=VirtualStore+FileName; + FilePath:=ExtractFilePath(FileName); + DelTree(FilePath, True, True, True); + end; +end; + function PrepareToInstall(var NeedsRestart: Boolean): String; begin if(IsAppRunning( 'pyfa.exe' )) then @@ -138,15 +150,19 @@ var V: Integer; iResultCode: Integer; sUnInstallString: string; - iOldVersion: Cardinal; + iOldVersionMajor: Cardinal; + iOldVersionMinor: Cardinal; begin Result := True; // in case when no previous version is found if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', 'UninstallString') then //Your App GUID/ID begin RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', - 'MinorVersion', iOldVersion); - if iOldVersion < {#VersionFlag} then // If old version with old structure is installed. + 'MajorVersion', iOldVersionMajor); + RegQueryDWordValue(HKEY_LOCAL_MACHINE, + 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', + 'MinorVersion', iOldVersionMinor); + if (iOldVersionMajor < {#MajorVersionFlag}) or ((iOldVersionMajor = {#MajorVersionFlag}) and (iOldVersionMinor < {#MinorVersionFlag})) then // If old version with old structure is installed. begin V := MsgBox(ExpandConstant('An old version of pyfa was detected. Due to recent changes in the application structure, you must uninstall the previous version first. This will not affect your user data (saved fittings, characters, etc.). Do you want to uninstall now?'), mbInformation, MB_YESNO); //Custom Message if App installed if V = IDYES then diff --git a/dist_assets/win/pyfa.exe.manifest b/dist_assets/win/pyfa.exe.manifest new file mode 100644 index 000000000..1086d2201 --- /dev/null +++ b/dist_assets/win/pyfa.exe.manifest @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist_assets/win/pyfa.spec b/dist_assets/win/pyfa.spec index 70eb4673d..1bdf820f0 100644 --- a/dist_assets/win/pyfa.spec +++ b/dist_assets/win/pyfa.spec @@ -5,8 +5,7 @@ from itertools import chain import subprocess import requests.certs -label = subprocess.check_output([ - "git", "describe", "--tags"]).strip() +label = subprocess.check_output(["git", "describe", "--tags"]).strip() with open('.version', 'w+') as f: f.write(label.decode()) @@ -18,16 +17,20 @@ added_files = [ ('../../imgs/gui/*.gif', 'imgs/gui'), ('../../imgs/icons/*.png', 'imgs/icons'), ('../../imgs/renders/*.png', 'imgs/renders'), - ('../../service/jargon/*.yaml', 'service/jargon'), + ('../../service/jargon/*.yaml', 'service/jargon'), ('../../dist_assets/win/pyfa.ico', '.'), + ('../../dist_assets/win/pyfa.exe.manifest', '.'), + ('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'), (requests.certs.where(), '.'), # is this needed anymore? ('../../eve.db', '.'), ('../../README.md', '.'), ('../../LICENSE', '.'), - ('../../.version', '.'), + ('../../version.yml', '.'), ] -import_these = [] +import_these = [ + 'numpy.core._dtype_ctypes' # https://github.com/pyinstaller/pyinstaller/issues/3982 +] # Walk directories that do dynamic importing paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') @@ -79,4 +82,5 @@ coll = COLLECT( upx=True, name='pyfa', icon='dist_assets/win/pyfa.ico', - ) \ No newline at end of file + ) + diff --git a/eos/config.py b/eos/config.py index 6bc31eef8..c6c53f748 100644 --- a/eos/config.py +++ b/eos/config.py @@ -11,6 +11,7 @@ debug = False gamedataCache = True saveddataCache = True gamedata_version = "" +gamedata_date = "" gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db")) pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index dd027841b..f02e9d944 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -51,10 +51,14 @@ try: config.gamedata_version = gamedata_session.execute( "SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'" ).fetchone()[0] + config.gamedata_date = gamedata_session.execute( + "SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'" + ).fetchone()[0] except Exception as e: pyfalog.warning("Missing gamedata version.") pyfalog.critical(e) config.gamedata_version = None + config.gamedata_date = None saveddata_connectionstring = config.saveddata_connectionstring if saveddata_connectionstring is not None: @@ -74,10 +78,10 @@ sd_lock = threading.RLock() # Import all the definitions for all our database stuff # noinspection PyPep8 -from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit +from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes # noinspection PyPep8 from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \ - miscData, module, override, price, queries, skill, targetResists, user + miscData, mutator, module, override, price, queries, skill, targetResists, user # Import queries # noinspection PyPep8 diff --git a/eos/db/gamedata/__init__.py b/eos/db/gamedata/__init__.py index eabfd7f1b..465433a26 100644 --- a/eos/db/gamedata/__init__.py +++ b/eos/db/gamedata/__init__.py @@ -1,2 +1,2 @@ -__all__ = ["attribute", "category", "effect", "group", "metaData", - "icon", "item", "marketGroup", "metaGroup", "unit", "alphaClones"] +__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes", + "item", "marketGroup", "metaGroup", "unit", "alphaClones"] diff --git a/eos/db/gamedata/attribute.py b/eos/db/gamedata/attribute.py index 20de4d1a9..727037421 100644 --- a/eos/db/gamedata/attribute.py +++ b/eos/db/gamedata/attribute.py @@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.gamedata import Attribute, AttributeInfo, Unit, Icon +from eos.gamedata import Attribute, AttributeInfo, Unit typeattributes_table = Table("dgmtypeattribs", gamedata_meta, Column("value", Float), @@ -38,7 +38,7 @@ attributes_table = Table("dgmattribs", gamedata_meta, Column("published", Boolean), Column("displayName", String), Column("highIsGood", Boolean), - Column("iconID", Integer, ForeignKey("icons.iconID")), + Column("iconID", Integer), Column("unitID", Integer, ForeignKey("dgmunits.unitID"))) mapper(Attribute, typeattributes_table, @@ -46,7 +46,6 @@ mapper(Attribute, typeattributes_table, mapper(AttributeInfo, attributes_table, properties={ - "icon" : relation(Icon), "unit" : relation(Unit), "ID" : synonym("attributeID"), "name" : synonym("attributeName"), diff --git a/eos/db/gamedata/category.py b/eos/db/gamedata/category.py index 0fd84da79..502f26c22 100644 --- a/eos/db/gamedata/category.py +++ b/eos/db/gamedata/category.py @@ -17,22 +17,21 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table -from sqlalchemy.orm import relation, mapper, synonym, deferred +from sqlalchemy import Boolean, Column, Integer, String, Table +from sqlalchemy.orm import deferred, mapper, synonym from eos.db import gamedata_meta -from eos.gamedata import Category, Icon +from eos.gamedata import Category categories_table = Table("invcategories", gamedata_meta, Column("categoryID", Integer, primary_key=True), Column("categoryName", String), Column("description", String), Column("published", Boolean), - Column("iconID", Integer, ForeignKey("icons.iconID"))) + Column("iconID", Integer)) mapper(Category, categories_table, properties={ - "icon" : relation(Icon), "ID" : synonym("categoryID"), "name" : synonym("categoryName"), "description": deferred(categories_table.c.description) diff --git a/eos/db/gamedata/dynamicAttributes.py b/eos/db/gamedata/dynamicAttributes.py new file mode 100644 index 000000000..26d25be0e --- /dev/null +++ b/eos/db/gamedata/dynamicAttributes.py @@ -0,0 +1,65 @@ +# =============================================================================== +# 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 . +# =============================================================================== + +from sqlalchemy import Column, Float, Integer, Table, ForeignKey +from sqlalchemy.orm import mapper, relation, synonym +from sqlalchemy.ext.associationproxy import association_proxy + +from eos.db import gamedata_meta +from eos.gamedata import DynamicItem, DynamicItemAttribute, DynamicItemItem, Item + +from eos.gamedata import AttributeInfo + +dynamic_table = Table("mutaplasmids", gamedata_meta, + Column("typeID", ForeignKey("invtypes.typeID"), primary_key=True, index=True), + Column("resultingTypeID", ForeignKey("invtypes.typeID"), primary_key=True)) + +dynamicAttributes_table = Table("mutaplasmidAttributes", gamedata_meta, + Column("typeID", Integer, ForeignKey("mutaplasmids.typeID"), primary_key=True), + Column("attributeID", ForeignKey("dgmattribs.attributeID"), primary_key=True), + Column("min", Float), + Column("max", Float)) + +dynamicApplicable_table = Table("mutaplasmidItems", gamedata_meta, + Column("typeID", ForeignKey("mutaplasmids.typeID"), primary_key=True), + Column("applicableTypeID", ForeignKey("invtypes.typeID"), primary_key=True),) + +mapper(DynamicItem, dynamic_table, properties={ + "attributes": relation(DynamicItemAttribute), + "item": relation(Item, foreign_keys=[dynamic_table.c.typeID]), + "resultingItem": relation(Item, foreign_keys=[dynamic_table.c.resultingTypeID]), + "ID": synonym("typeID"), +}) + +mapper(DynamicItemAttribute, dynamicAttributes_table, + properties={"info": relation(AttributeInfo, lazy=False)}) + +mapper(DynamicItemItem, dynamicApplicable_table, properties={ + "mutaplasmid": relation(DynamicItem), + }) + +DynamicItemAttribute.ID = association_proxy("info", "attributeID") +DynamicItemAttribute.name = association_proxy("info", "attributeName") +DynamicItemAttribute.description = association_proxy("info", "description") +DynamicItemAttribute.published = association_proxy("info", "published") +DynamicItemAttribute.displayName = association_proxy("info", "displayName") +DynamicItemAttribute.highIsGood = association_proxy("info", "highIsGood") +DynamicItemAttribute.iconID = association_proxy("info", "iconID") +DynamicItemAttribute.icon = association_proxy("info", "icon") +DynamicItemAttribute.unit = association_proxy("info", "unit") diff --git a/eos/db/gamedata/group.py b/eos/db/gamedata/group.py index 4373b2ca5..a9a66a8ef 100644 --- a/eos/db/gamedata/group.py +++ b/eos/db/gamedata/group.py @@ -18,10 +18,10 @@ # =============================================================================== from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table -from sqlalchemy.orm import relation, mapper, synonym, deferred +from sqlalchemy.orm import relation, mapper, synonym, deferred, backref from eos.db import gamedata_meta -from eos.gamedata import Category, Group, Icon +from eos.gamedata import Category, Group groups_table = Table("invgroups", gamedata_meta, Column("groupID", Integer, primary_key=True), @@ -29,12 +29,11 @@ groups_table = Table("invgroups", gamedata_meta, Column("description", String), Column("published", Boolean), Column("categoryID", Integer, ForeignKey("invcategories.categoryID")), - Column("iconID", Integer, ForeignKey("icons.iconID"))) + Column("iconID", Integer)) mapper(Group, groups_table, properties={ - "category" : relation(Category, backref="groups"), - "icon" : relation(Icon), + "category" : relation(Category, backref=backref("groups", cascade="all,delete")), "ID" : synonym("groupID"), "name" : synonym("groupName"), "description": deferred(groups_table.c.description) diff --git a/eos/db/gamedata/icon.py b/eos/db/gamedata/icon.py deleted file mode 100644 index 9fd41605a..000000000 --- a/eos/db/gamedata/icon.py +++ /dev/null @@ -1,35 +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 . -# =============================================================================== - -from sqlalchemy import Column, String, Integer, Table -from sqlalchemy.orm import mapper, synonym, deferred - -from eos.db import gamedata_meta -from eos.gamedata import Icon - -icons_table = Table("icons", gamedata_meta, - Column("iconID", Integer, primary_key=True), - Column("description", String), - Column("iconFile", String)) - -mapper(Icon, icons_table, - properties={ - "ID" : synonym("iconID"), - "description": deferred(icons_table.c.description) - }) diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py index 1760f9157..fd7be477d 100644 --- a/eos/db/gamedata/item.py +++ b/eos/db/gamedata/item.py @@ -17,14 +17,15 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table, Float +from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import relation, mapper, synonym, deferred +from sqlalchemy.orm import backref, deferred, mapper, relation, synonym from sqlalchemy.orm.collections import attribute_mapped_collection -from eos.db.gamedata.effect import typeeffects_table from eos.db import gamedata_meta -from eos.gamedata import Attribute, Effect, Group, Icon, Item, MetaType, Traits +from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table +from eos.db.gamedata.effect import typeeffects_table +from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, MetaType, Traits items_table = Table("invtypes", gamedata_meta, Column("typeID", Integer, primary_key=True), @@ -37,16 +38,18 @@ items_table = Table("invtypes", gamedata_meta, Column("capacity", Float), Column("published", Boolean), Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")), - Column("iconID", Integer, ForeignKey("icons.iconID")), - Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True)) + Column("iconID", Integer), + Column("graphicID", Integer), + Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True), + Column("replaceSame", String), + Column("replaceBetter", String)) from .metaGroup import metatypes_table # noqa from .traits import traits_table # noqa mapper(Item, items_table, properties={ - "group" : relation(Group, backref="items"), - "icon" : relation(Icon), + "group" : relation(Group, backref=backref("items", cascade="all,delete")), "_Item__attributes": relation(Attribute, cascade='all, delete, delete-orphan', collection_class=attribute_mapped_collection('name')), "effects": relation(Effect, secondary=typeeffects_table, collection_class=attribute_mapped_collection('name')), "metaGroup" : relation(MetaType, @@ -57,7 +60,12 @@ mapper(Item, items_table, "description" : deferred(items_table.c.description), "traits" : relation(Traits, primaryjoin=traits_table.c.typeID == items_table.c.typeID, - uselist=False) + uselist=False), + "mutaplasmids": relation(DynamicItem, + primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID, + secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID, + secondary=dynamicApplicable_table, + backref="applicableItems") }) Item.category = association_proxy("group", "category") diff --git a/eos/db/gamedata/marketGroup.py b/eos/db/gamedata/marketGroup.py index faf88780b..8bd04f401 100644 --- a/eos/db/gamedata/marketGroup.py +++ b/eos/db/gamedata/marketGroup.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.gamedata import Icon, Item, MarketGroup +from eos.gamedata import Item, MarketGroup marketgroups_table = Table("invmarketgroups", gamedata_meta, Column("marketGroupID", Integer, primary_key=True), @@ -30,14 +30,13 @@ marketgroups_table = Table("invmarketgroups", gamedata_meta, Column("hasTypes", Boolean), Column("parentGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID", initially="DEFERRED", deferrable=True)), - Column("iconID", Integer, ForeignKey("icons.iconID"))) + Column("iconID", Integer)) mapper(MarketGroup, marketgroups_table, properties={ "items" : relation(Item, backref="marketGroup"), "parent" : relation(MarketGroup, backref="children", remote_side=[marketgroups_table.c.marketGroupID]), - "icon" : relation(Icon), "ID" : synonym("marketGroupID"), "name" : synonym("marketGroupName"), "description": deferred(marketgroups_table.c.description) diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 737169897..a026704cb 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -17,15 +17,16 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy.orm import join, exc, aliased +from sqlalchemy.inspection import inspect +from sqlalchemy.orm import aliased, exc, join from sqlalchemy.sql import and_, or_, select import eos.config from eos.db import gamedata_session -from eos.db.gamedata.metaGroup import metatypes_table, items_table from eos.db.gamedata.group import groups_table +from eos.db.gamedata.metaGroup import items_table, metatypes_table from eos.db.util import processEager, processWhere -from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData +from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup cache = {} configVal = getattr(eos.config, "gamedataCache", None) @@ -97,6 +98,36 @@ def getItem(lookfor, eager=None): return item +def getMutaplasmid(lookfor, eager=None): + if isinstance(lookfor, int): + item = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == lookfor).first() + else: + raise TypeError("Need integer as argument") + return item + + +def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None): + # A lot of this is described in more detail in #1597 + item = gamedata_session.query(Item).get(lookfor) + base = getItem(baseItemID) + + # we have to load all attributes for this object, otherwise we'll lose access to them when we expunge. + # todo: figure out a way to eagerly load all these via the query... + for x in [*inspect(Item).relationships.keys(), 'description']: + getattr(item, x) + + # Copy over the attributes from the base, but ise the items attributes when there's an overlap + # WARNING: the attribute object still has the old typeID. I don't believe we access this typeID anywhere in the code, + # but should keep this in mind for now. + item._Item__attributes = {**base.attributes, **item.attributes} + + # Expunge the item form the session. This is required to have different Abyssal / Base combinations loaded in memory. + # Without expunging it, once one Abyssal Web is created, SQLAlchmey will use it for all others. We don't want this, + # we want to generate a completely new object to work with + gamedata_session.expunge(item) + return item + + @cachedQuery(1, "lookfor") def getItems(lookfor, eager=None): """ @@ -361,6 +392,25 @@ def directAttributeRequest(itemIDs, attrIDs): return result +def getAbyssalTypes(): + return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()]) + + +@cachedQuery(1, "itemID") +def getDynamicItem(itemID, eager=None): + try: + if isinstance(itemID, int): + if eager is None: + result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one() + else: + result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one() + else: + raise TypeError("Need integer as argument") + except exc.NoResultFound: + result = None + return result + + def getRequiredFor(itemID, attrMapping): Attribute1 = aliased(Attribute) Attribute2 = aliased(Attribute) diff --git a/eos/db/migrations/upgrade22.py b/eos/db/migrations/upgrade22.py index 568351a25..5a9b01e34 100644 --- a/eos/db/migrations/upgrade22.py +++ b/eos/db/migrations/upgrade22.py @@ -14,7 +14,7 @@ def upgrade(saveddata_engine): "boosters": 2, "cargo": 2, "characters": 2, - "crest": 1, + # "crest": 1, "damagePatterns": 2, "drones": 2, "fighters": 2, diff --git a/eos/db/migrations/upgrade28.py b/eos/db/migrations/upgrade28.py new file mode 100644 index 000000000..2ca735414 --- /dev/null +++ b/eos/db/migrations/upgrade28.py @@ -0,0 +1,18 @@ +""" +Migration 28 + +- adds baseItemID and mutaplasmidID to modules table +""" +import sqlalchemy + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT baseItemID FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN baseItemID INT;") + + try: + saveddata_engine.execute("SELECT mutaplasmidID FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN mutaplasmidID INT;") diff --git a/eos/db/migrations/upgrade29.py b/eos/db/migrations/upgrade29.py new file mode 100644 index 000000000..d94365236 --- /dev/null +++ b/eos/db/migrations/upgrade29.py @@ -0,0 +1,18 @@ +""" +Migration 29 + +- adds spoolType and spoolAmount to modules table +""" +import sqlalchemy + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT spoolType FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN spoolType INT;") + + try: + saveddata_engine.execute("SELECT spoolAmount FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN spoolAmount FLOAT;") diff --git a/eos/db/migrations/upgrade30.py b/eos/db/migrations/upgrade30.py new file mode 100644 index 000000000..7954f2d37 --- /dev/null +++ b/eos/db/migrations/upgrade30.py @@ -0,0 +1,17 @@ +""" +Migration 30 + +- changes to prices table +""" + + +import sqlalchemy + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT status FROM prices LIMIT 1") + except sqlalchemy.exc.DatabaseError: + # Just drop table, table will be re-created by sqlalchemy and + # data will be re-fetched + saveddata_engine.execute("DROP TABLE prices;") diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index ba1ddad73..c36517623 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -1,6 +1,7 @@ __all__ = [ "character", "fit", + "mutator", "module", "user", "skill", diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index 739a34a98..850c41c6e 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -29,9 +29,6 @@ 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), diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index 07375dcaf..dbee2c622 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -17,33 +17,32 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy.sql import and_ -from sqlalchemy.orm import relation, reconstructor, mapper, relationship -from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean, DateTime import datetime -from eos.db import saveddata_meta -from eos.db import saveddata_session +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Table +from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.orm import mapper, reconstructor, relation, relationship +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.sql import and_ + +from eos.db import saveddata_meta, saveddata_session from eos.db.saveddata.cargo import cargo_table from eos.db.saveddata.drone import drones_table from eos.db.saveddata.fighter import fighters_table from eos.db.saveddata.implant import fitImplants_table from eos.db.saveddata.module import modules_table -from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \ - HandledDroneCargoList, HandledProjectedDroneList -from eos.saveddata.implant import Implant -from eos.saveddata.character import Character -from eos.saveddata.user import User -from eos.saveddata.fighter import Fighter -from eos.saveddata.fit import Fit as es_Fit, ImplantLocation -from eos.saveddata.drone import Drone +from eos.effectHandlerHelpers import HandledDroneCargoList, HandledImplantBoosterList, HandledModuleList, HandledProjectedDroneList, HandledProjectedModList from eos.saveddata.booster import Booster -from eos.saveddata.module import Module from eos.saveddata.cargo import Cargo +from eos.saveddata.character import Character from eos.saveddata.damagePattern import DamagePattern +from eos.saveddata.drone import Drone +from eos.saveddata.fighter import Fighter +from eos.saveddata.fit import Fit as es_Fit +from eos.saveddata.implant import Implant +from eos.saveddata.module import Module from eos.saveddata.targetResists import TargetResists +from eos.saveddata.user import User fits_table = Table("fits", saveddata_meta, Column("ID", Integer, primary_key=True), @@ -56,7 +55,7 @@ fits_table = Table("fits", saveddata_meta, Column("booster", Boolean, nullable=False, index=True, default=0), Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True), Column("modeID", Integer, nullable=True), - Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT), + Column("implantLocation", Integer, nullable=False), Column("notes", String, nullable=True), Column("ignoreRestrictions", Boolean, default=0), Column("created", DateTime, nullable=True, default=datetime.datetime.now), @@ -132,13 +131,13 @@ class CommandFit(object): ) -es_Fit._Fit__projectedFits = association_proxy( +es_Fit.projectedFitDict = association_proxy( "victimOf", # look at the victimOf association... "source_fit", # .. and return the source fits creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit) ) -es_Fit._Fit__commandFits = association_proxy( +es_Fit.commandFitDict = association_proxy( "boostedOf", # look at the boostedOf association... "booster_fit", # .. and return the booster fit creator=lambda boosterID, booster_fit: CommandFit(boosterID, booster_fit) diff --git a/eos/db/saveddata/module.py b/eos/db/saveddata/module.py index 149f4f73c..a0e511127 100644 --- a/eos/db/saveddata/module.py +++ b/eos/db/saveddata/module.py @@ -17,18 +17,22 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Table, Column, Integer, ForeignKey, CheckConstraint, Boolean, DateTime +from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime +from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.orm import relation, mapper import datetime from eos.db import saveddata_meta from eos.saveddata.module import Module +from eos.saveddata.mutator import Mutator from eos.saveddata.fit import Fit modules_table = Table("modules", saveddata_meta, Column("ID", Integer, primary_key=True), Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True), Column("itemID", Integer, nullable=True), + Column("baseItemID", Integer, nullable=True), + Column("mutaplasmidID", Integer, nullable=True), Column("dummySlot", Integer, nullable=True, default=None), Column("chargeID", Integer), Column("state", Integer, CheckConstraint("state >= -1"), CheckConstraint("state <= 2")), @@ -36,7 +40,17 @@ modules_table = Table("modules", saveddata_meta, Column("position", Integer), Column("created", DateTime, nullable=True, default=datetime.datetime.now), Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now), + Column("spoolType", Integer, nullable=True), + Column("spoolAmount", Float, nullable=True), CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"')) mapper(Module, modules_table, - properties={"owner": relation(Fit)}) + properties={ + "owner": relation(Fit), + "mutators": relation( + Mutator, + backref="module", + cascade="all,delete-orphan", + collection_class=attribute_mapped_collection('attrID') + ) + }) diff --git a/eos/db/saveddata/mutator.py b/eos/db/saveddata/mutator.py new file mode 100644 index 000000000..1ba81e097 --- /dev/null +++ b/eos/db/saveddata/mutator.py @@ -0,0 +1,35 @@ +# =============================================================================== +# 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 . +# =============================================================================== + +import datetime + +from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table +from sqlalchemy.orm import mapper + +from eos.db import saveddata_meta +from eos.saveddata.mutator import Mutator + +mutator_table = Table("mutators", saveddata_meta, + Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True), + Column("attrID", Integer, primary_key=True, index=True), + Column("value", Float, nullable=False), + Column("created", DateTime, nullable=True, default=datetime.datetime.now), + Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)) + +mapper(Mutator, mutator_table) diff --git a/eos/db/saveddata/price.py b/eos/db/saveddata/price.py index 8be7f6519..8abd07132 100644 --- a/eos/db/saveddata/price.py +++ b/eos/db/saveddata/price.py @@ -17,17 +17,20 @@ # along with eos. If not, see . # =============================================================================== + from sqlalchemy import Table, Column, Float, Integer from sqlalchemy.orm import mapper from eos.db import saveddata_meta from eos.saveddata.price import Price + prices_table = Table("prices", saveddata_meta, Column("typeID", Integer, primary_key=True), Column("price", Float, default=0.0), Column("time", Integer, nullable=False), - Column("failed", Integer)) + Column("status", Integer, nullable=False)) + mapper(Price, prices_table, properties={ "_Price__price": prices_table.c.price, diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index e582eef87..448584420 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -542,8 +542,17 @@ def commit(): with sd_lock: try: saveddata_session.commit() - saveddata_session.flush() - except Exception as ex: + except Exception: + saveddata_session.rollback() + exc_info = sys.exc_info() + raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) + + +def flush(): + with sd_lock: + try: + saveddata_session.flush() + except Exception: saveddata_session.rollback() exc_info = sys.exc_info() raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index f890ae0ed..efe4d7e38 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -18,6 +18,7 @@ # =============================================================================== from logbook import Logger +from utils.deprecated import deprecated pyfalog = Logger(__name__) @@ -113,6 +114,7 @@ class HandledList(list): class HandledModuleList(HandledList): + def append(self, mod): emptyPosition = float("Inf") for i in range(len(self)): @@ -130,12 +132,32 @@ class HandledModuleList(HandledList): self.remove(mod) return + self.appendIgnoreEmpty(mod) + + def appendIgnoreEmpty(self, mod): mod.position = len(self) HandledList.append(self, mod) if mod.isInvalid: self.remove(mod) return + def replaceRackPosition(self, rackPosition, mod): + listPositions = [] + for currMod in self: + if currMod.slot == mod.slot: + listPositions.append(currMod.position) + listPositions.sort() + try: + modListPosition = listPositions[rackPosition] + except IndexError: + self.appendIgnoreEmpty(mod) + else: + self.toDummy(modListPosition) + if not mod.isEmpty: + self.toModule(modListPosition, mod) + if mod.isInvalid: + self.toDummy(modListPosition) + def insert(self, index, mod): mod.position = index i = index @@ -163,6 +185,7 @@ class HandledModuleList(HandledList): mod.position = index self[index] = mod + @deprecated def freeSlot(self, slot): for i in range(len(self)): mod = self[i] @@ -195,14 +218,20 @@ class HandledImplantBoosterList(HandledList): self.remove(thing) return + self.makeRoom(thing) + HandledList.append(self, thing) + + def makeRoom(self, thing): # if needed, remove booster that was occupying slot oldObj = next((m for m in self if m.slot == thing.slot), None) if oldObj: - pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name) + pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, + thing.item.name) + itemID = oldObj.itemID oldObj.itemID = 0 # hack to remove from DB. See GH issue #324 self.remove(oldObj) - - HandledList.append(self, thing) + return itemID + return None class HandledSsoCharacterList(list): @@ -225,22 +254,30 @@ class HandledProjectedModList(HandledList): return proj.projected = True - isSystemEffect = proj.item.group.name == "Effect Beacon" - if isSystemEffect: - # remove other system effects - only 1 per fit plz - oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None) - - if oldEffect: - pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name) - self.remove(oldEffect) + if proj.isExclusiveSystemEffect: + self.makeRoom(proj) HandledList.append(self, proj) # Remove non-projectable modules - if not proj.item.isType("projected") and not isSystemEffect: + if not proj.item.isType("projected") and not proj.isExclusiveSystemEffect: self.remove(proj) + @property + def currentSystemEffect(self): + return next((m for m in self if m.isExclusiveSystemEffect), None) + + def makeRoom(self, proj): + # remove other system effects - only 1 per fit plz + oldEffect = self.currentSystemEffect + + if oldEffect: + pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name) + self.remove(oldEffect) + return oldEffect.itemID + return None + class HandledProjectedDroneList(HandledDroneCargoList): def append(self, proj): diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index 246c67fcf..25a4335a6 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -13,11 +13,14 @@ type = "active" def handler(fit, module, context): damagePattern = fit.damagePattern + # pyfalog.debug("==============================") static_adaptive_behavior = eos.config.settings['useStaticAdaptiveArmorHardener'] if (damagePattern.emAmount == damagePattern.thermalAmount == damagePattern.kineticAmount == damagePattern.explosiveAmount) and static_adaptive_behavior: - pyfalog.debug("Setting adaptivearmorhardener resists to uniform profile.") + # pyfalog.debug("Setting adaptivearmorhardener resists to uniform profile.") + for attr in ("armorEmDamageResonance", "armorThermalDamageResonance", "armorKineticDamageResonance", "armorExplosiveDamageResonance"): + fit.ship.multiplyItemAttr(attr, module.getModifiedItemAttr(attr), stackingPenalties=True, penaltyGroup="preMul") return # Skip if there is no damage pattern. Example: projected ships or fleet boosters @@ -30,7 +33,7 @@ def handler(fit, module, context): damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), ) - # pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + # pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f" % (baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])) resistanceShiftAmount = module.getModifiedItemAttr( 'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction @@ -46,7 +49,7 @@ def handler(fit, module, context): cycleList = [] loopStart = -20 for num in range(50): - # pyfalog.debug("Starting cycle %d.", num) + # pyfalog.debug("Starting cycle %d." % num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. # This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result. damagePattern_tuples = [ @@ -84,7 +87,7 @@ def handler(fit, module, context): RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 - # pyfalog.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + # pyfalog.debug("Resistances shifted to %f/%f/%f/%f" % ( RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3])) # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): @@ -94,16 +97,16 @@ def handler(fit, module, context): abs(RAHResistance[2] - val[2]) <= tolerance and \ abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - # pyfalog.debug("Loop found: %d-%d", loopStart, num) + # pyfalog.debug("Loop found: %d-%d" % (loopStart, num)) break if loopStart >= 0: break cycleList.append(list(RAHResistance)) - if loopStart < 0: - pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}", - baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + # if loopStart < 0: + # pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}".format( + # baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])) # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. loopCycles = cycleList[loopStart:] @@ -117,7 +120,7 @@ def handler(fit, module, context): average[i] = round(average[i] / numCycles, 3) # Set the new resistances - # pyfalog.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + # pyfalog.debug("Setting new resist profile: %f/%f/%f/%f" % ( average[0], average[1], average[2],average[3])) for i, attr in enumerate(( 'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): diff --git a/eos/effects/addtosignatureradius2.py b/eos/effects/addtosignatureradius2.py index 550357bfc..6c790a925 100644 --- a/eos/effects/addtosignatureradius2.py +++ b/eos/effects/addtosignatureradius2.py @@ -2,7 +2,7 @@ # # Used by: # Modules from group: Missile Launcher Bomb (2 of 2) -# Modules from group: Shield Extender (33 of 33) +# Modules from group: Shield Extender (36 of 36) type = "passive" diff --git a/eos/effects/agilitymultipliereffectpassive.py b/eos/effects/agilitymultipliereffectpassive.py index affa839e8..d185a5002 100644 --- a/eos/effects/agilitymultipliereffectpassive.py +++ b/eos/effects/agilitymultipliereffectpassive.py @@ -6,4 +6,4 @@ type = "passive" def handler(fit, module, context): - fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("agilityMultiplier"), stackingPenalties=True) + fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("agilityBonus"), stackingPenalties=True) diff --git a/eos/effects/ammoinfluencecapneed.py b/eos/effects/ammoinfluencecapneed.py index b798f0f4b..3bbb1103c 100644 --- a/eos/effects/ammoinfluencecapneed.py +++ b/eos/effects/ammoinfluencecapneed.py @@ -1,7 +1,7 @@ # ammoInfluenceCapNeed # # Used by: -# Items from category: Charge (478 of 928) +# Items from category: Charge (493 of 949) type = "passive" diff --git a/eos/effects/ammoinfluencerange.py b/eos/effects/ammoinfluencerange.py index 078b2b205..58c331911 100644 --- a/eos/effects/ammoinfluencerange.py +++ b/eos/effects/ammoinfluencerange.py @@ -1,7 +1,7 @@ # ammoInfluenceRange # # Used by: -# Items from category: Charge (572 of 928) +# Items from category: Charge (587 of 949) type = "passive" diff --git a/eos/effects/ammospeedmultiplier.py b/eos/effects/ammospeedmultiplier.py index ac7ada2ad..093953c11 100644 --- a/eos/effects/ammospeedmultiplier.py +++ b/eos/effects/ammospeedmultiplier.py @@ -1,9 +1,9 @@ # ammoSpeedMultiplier # # Used by: -# Charges from group: Festival Charges (23 of 23) +# Charges from group: Festival Charges (26 of 26) # Charges from group: Interdiction Probe (2 of 2) -# Charges from group: Structure Festival Charges (3 of 3) +# Items from market group: Special Edition Assets > Special Edition Festival Assets (30 of 33) type = "passive" diff --git a/eos/effects/ammotrackingmultiplier.py b/eos/effects/ammotrackingmultiplier.py index 0cea10c80..95033d743 100644 --- a/eos/effects/ammotrackingmultiplier.py +++ b/eos/effects/ammotrackingmultiplier.py @@ -1,12 +1,7 @@ # ammoTrackingMultiplier # # Used by: -# Charges from group: Advanced Artillery Ammo (8 of 8) -# Charges from group: Advanced Autocannon Ammo (8 of 8) -# Charges from group: Advanced Beam Laser Crystal (8 of 8) -# Charges from group: Advanced Blaster Charge (8 of 8) -# Charges from group: Advanced Pulse Laser Crystal (8 of 8) -# Charges from group: Advanced Railgun Charge (8 of 8) +# Items from category: Charge (182 of 949) # Charges from group: Projectile Ammo (128 of 128) type = "passive" diff --git a/eos/effects/aoebeaconbioluminescencecloud.py b/eos/effects/aoebeaconbioluminescencecloud.py new file mode 100644 index 000000000..fa01e217d --- /dev/null +++ b/eos/effects/aoebeaconbioluminescencecloud.py @@ -0,0 +1,16 @@ +# aoe_beacon_bioluminescence_cloud +# +# Used by: +# Celestials named like: Bioluminescence Cloud (3 of 3) +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/aoebeaconcausticcloud.py b/eos/effects/aoebeaconcausticcloud.py new file mode 100644 index 000000000..f11c9805b --- /dev/null +++ b/eos/effects/aoebeaconcausticcloud.py @@ -0,0 +1,16 @@ +# aoe_beacon_caustic_cloud +# +# Used by: +# Celestials named like: Caustic Cloud (3 of 3) +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/aoebeaconfilamentcloud.py b/eos/effects/aoebeaconfilamentcloud.py new file mode 100644 index 000000000..085114e7c --- /dev/null +++ b/eos/effects/aoebeaconfilamentcloud.py @@ -0,0 +1,16 @@ +# aoe_beacon_filament_cloud +# +# Used by: +# Celestials named like: Filament Cloud (3 of 3) +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/armorallrepairsystemsamountbonuspassive.py b/eos/effects/armorallrepairsystemsamountbonuspassive.py index c5162f3e0..8307ae2ee 100644 --- a/eos/effects/armorallrepairsystemsamountbonuspassive.py +++ b/eos/effects/armorallrepairsystemsamountbonuspassive.py @@ -1,6 +1,7 @@ # armorAllRepairSystemsAmountBonusPassive # # Used by: +# Implants named like: Agency 'Hardshell' TB Dose (4 of 4) # Implants named like: Exile Booster (4 of 4) # Implant: Antipharmakon Kosybo type = "passive" @@ -9,4 +10,4 @@ type = "passive" def handler(fit, booster, context): fit.modules.filteredItemBoost( lambda mod: mod.item.requiresSkill("Repair Systems") or mod.item.requiresSkill("Capital Repair Systems"), - "armorDamageAmount", booster.getModifiedItemAttr("armorDamageAmountBonus")) + "armorDamageAmount", booster.getModifiedItemAttr("armorDamageAmountBonus") or 0) diff --git a/eos/effects/armorhpbonusadd.py b/eos/effects/armorhpbonusadd.py index 591e1cc58..f965b45ae 100644 --- a/eos/effects/armorhpbonusadd.py +++ b/eos/effects/armorhpbonusadd.py @@ -1,7 +1,7 @@ # armorHPBonusAdd # # Used by: -# Modules from group: Armor Reinforcer (48 of 48) +# Modules from group: Armor Reinforcer (51 of 51) type = "passive" diff --git a/eos/effects/armorreinforcermassadd.py b/eos/effects/armorreinforcermassadd.py index d61b0224c..e60326892 100644 --- a/eos/effects/armorreinforcermassadd.py +++ b/eos/effects/armorreinforcermassadd.py @@ -1,7 +1,7 @@ # armorReinforcerMassAdd # # Used by: -# Modules from group: Armor Reinforcer (48 of 48) +# Modules from group: Armor Reinforcer (51 of 51) type = "passive" diff --git a/eos/effects/armorrepair.py b/eos/effects/armorrepair.py index 8555baec0..07c1926da 100644 --- a/eos/effects/armorrepair.py +++ b/eos/effects/armorrepair.py @@ -1,7 +1,7 @@ # armorRepair # # Used by: -# Modules from group: Armor Repair Unit (105 of 105) +# Modules from group: Armor Repair Unit (108 of 108) runTime = "late" type = "active" @@ -9,4 +9,7 @@ type = "active" def handler(fit, module, context): amount = module.getModifiedItemAttr("armorDamageAmount") speed = module.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", amount / speed) + rps = amount / speed + fit.extraAttributes.increase("armorRepair", rps) + fit.extraAttributes.increase("armorRepairPreSpool", rps) + fit.extraAttributes.increase("armorRepairFullSpool", rps) diff --git a/eos/effects/armorwarfarearmorhpreplacer.py b/eos/effects/armorwarfarearmorhpreplacer.py deleted file mode 100644 index 4cbe460c0..000000000 --- a/eos/effects/armorwarfarearmorhpreplacer.py +++ /dev/null @@ -1,10 +0,0 @@ -# Not used by any item -type = "gang", "active" -gangBonus = "armorHpBonus2" -gangBoost = "armorHP" - - -def handler(fit, module, context): - if "gang" not in context: - return - fit.ship.boostItemAttr("armorHP", module.getModifiedItemAttr("armorHpBonus2")) diff --git a/eos/effects/boosterarmorhppenalty.py b/eos/effects/boosterarmorhppenalty.py index 63fb57cd1..0620ca719 100644 --- a/eos/effects/boosterarmorhppenalty.py +++ b/eos/effects/boosterarmorhppenalty.py @@ -1,7 +1,7 @@ # boosterArmorHpPenalty # # Used by: -# Implants from group: Booster (12 of 62) +# Implants named like: Booster (12 of 35) type = "boosterSideEffect" # User-friendly name for the side effect diff --git a/eos/effects/boostermaxvelocitypenalty.py b/eos/effects/boostermaxvelocitypenalty.py index b6decaf9f..83856e018 100644 --- a/eos/effects/boostermaxvelocitypenalty.py +++ b/eos/effects/boostermaxvelocitypenalty.py @@ -1,7 +1,8 @@ # boosterMaxVelocityPenalty # # Used by: -# Implants from group: Booster (12 of 62) +# Implants named like: Crash Booster (3 of 4) +# Items from market group: Implants & Boosters > Booster > Booster Slot 02 (9 of 13) type = "boosterSideEffect" # User-friendly name for the side effect diff --git a/eos/effects/boostermodifyboosterarmorpenalties.py b/eos/effects/boostermodifyboosterarmorpenalties.py index a8b30251b..0714422d4 100644 --- a/eos/effects/boostermodifyboosterarmorpenalties.py +++ b/eos/effects/boostermodifyboosterarmorpenalties.py @@ -4,6 +4,7 @@ # Implants named like: Eifyr and Co. 'Alchemist' Neurotoxin Control NC (2 of 2) # Implants named like: grade Edge (10 of 12) # Skill: Neurotoxin Control +runTime = 'early' type = "passive" diff --git a/eos/effects/boostershieldcapacitypenalty.py b/eos/effects/boostershieldcapacitypenalty.py index 7bc929983..f93f04af9 100644 --- a/eos/effects/boostershieldcapacitypenalty.py +++ b/eos/effects/boostershieldcapacitypenalty.py @@ -1,7 +1,7 @@ # boosterShieldCapacityPenalty # # Used by: -# Implants from group: Booster (12 of 62) +# Implants from group: Booster (12 of 70) type = "boosterSideEffect" # User-friendly name for the side effect diff --git a/eos/effects/capacitorcapacitybonus.py b/eos/effects/capacitorcapacitybonus.py index f2c0632da..c998bcaa0 100644 --- a/eos/effects/capacitorcapacitybonus.py +++ b/eos/effects/capacitorcapacitybonus.py @@ -1,7 +1,7 @@ # capacitorCapacityBonus # # Used by: -# Modules from group: Capacitor Battery (27 of 27) +# Modules from group: Capacitor Battery (30 of 30) type = "passive" diff --git a/eos/effects/capacitorcapacitymultiply.py b/eos/effects/capacitorcapacitymultiply.py index 8b5df217d..91f3e6ed4 100644 --- a/eos/effects/capacitorcapacitymultiply.py +++ b/eos/effects/capacitorcapacitymultiply.py @@ -4,7 +4,7 @@ # Modules from group: Capacitor Flux Coil (6 of 6) # Modules from group: Capacitor Power Relay (20 of 20) # Modules from group: Power Diagnostic System (23 of 23) -# Modules from group: Propulsion Module (65 of 127) +# Modules from group: Propulsion Module (68 of 133) # Modules from group: Reactor Control Unit (22 of 22) type = "passive" diff --git a/eos/effects/citadelrigbonus.py b/eos/effects/citadelrigbonus.py index a04c6f479..195835283 100644 --- a/eos/effects/citadelrigbonus.py +++ b/eos/effects/citadelrigbonus.py @@ -1,4 +1,7 @@ -# Not used by any item +# citadelRigBonus +# +# Used by: +# Structures from group: Citadel (9 of 9) type = "passive" runTime = "early" diff --git a/eos/effects/commandburstaoerolebonus.py b/eos/effects/commandburstaoerolebonus.py index aa7fe44ce..f69aecd01 100644 --- a/eos/effects/commandburstaoerolebonus.py +++ b/eos/effects/commandburstaoerolebonus.py @@ -2,7 +2,7 @@ # # Used by: # Ships from group: Carrier (4 of 4) -# Ships from group: Combat Battlecruiser (13 of 13) +# Ships from group: Combat Battlecruiser (14 of 14) # Ships from group: Command Ship (8 of 8) # Ships from group: Force Auxiliary (6 of 6) # Ships from group: Supercarrier (6 of 6) diff --git a/eos/effects/covertopsandreconopscloakmoduledelaybonus.py b/eos/effects/covertopsandreconopscloakmoduledelaybonus.py index a808a2650..7ea8e593d 100644 --- a/eos/effects/covertopsandreconopscloakmoduledelaybonus.py +++ b/eos/effects/covertopsandreconopscloakmoduledelaybonus.py @@ -3,9 +3,9 @@ # Used by: # Ships from group: Black Ops (5 of 5) # Ships from group: Blockade Runner (4 of 4) -# Ships from group: Covert Ops (7 of 7) +# Ships from group: Covert Ops (8 of 8) # Ships from group: Expedition Frigate (2 of 2) -# Ships from group: Force Recon Ship (8 of 8) +# Ships from group: Force Recon Ship (9 of 9) # Ships from group: Stealth Bomber (5 of 5) # Ships named like: Stratios (2 of 2) # Subsystems named like: Defensive Covert Reconfiguration (4 of 4) diff --git a/eos/effects/covertopscloakcpupercentbonus1.py b/eos/effects/covertopscloakcpupercentbonus1.py index 703b37189..e34450131 100644 --- a/eos/effects/covertopscloakcpupercentbonus1.py +++ b/eos/effects/covertopscloakcpupercentbonus1.py @@ -1,7 +1,7 @@ # covertOpsCloakCpuPercentBonus1 # # Used by: -# Ships from group: Covert Ops (5 of 7) +# Ships from group: Covert Ops (6 of 8) type = "passive" runTime = "early" diff --git a/eos/effects/covertopswarpresistance.py b/eos/effects/covertopswarpresistance.py new file mode 100644 index 000000000..1ab7538c1 --- /dev/null +++ b/eos/effects/covertopswarpresistance.py @@ -0,0 +1,9 @@ +# covertOpsWarpResistance +# +# Used by: +# Ships from group: Covert Ops (5 of 8) +type = "passive" + + +def handler(fit, src, context): + fit.ship.increaseItemAttr("warpFactor", src.getModifiedItemAttr("eliteBonusCovertOps1"), skill="Covert Ops") diff --git a/eos/effects/cpumultiplierpostmulcpuoutputship.py b/eos/effects/cpumultiplierpostmulcpuoutputship.py index 99f186ce7..99c14306b 100644 --- a/eos/effects/cpumultiplierpostmulcpuoutputship.py +++ b/eos/effects/cpumultiplierpostmulcpuoutputship.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: CPU Enhancer (19 of 19) +# Variations of structure module: Standup Co-Processor Array I (2 of 2) type = "passive" diff --git a/eos/effects/crystalminingamountinfo2.py b/eos/effects/crystalminingamountinfo2.py index 5c7305860..68a126d49 100644 --- a/eos/effects/crystalminingamountinfo2.py +++ b/eos/effects/crystalminingamountinfo2.py @@ -3,7 +3,7 @@ # Used by: # Modules from group: Frequency Mining Laser (3 of 3) type = "passive" - +runTime = "late" def handler(fit, module, context): module.preAssignItemAttr("specialtyMiningAmount", module.getModifiedItemAttr("miningAmount")) diff --git a/eos/effects/cynosuraldurationbonus.py b/eos/effects/cynosuraldurationbonus.py index cb306dded..defbd9ca3 100644 --- a/eos/effects/cynosuraldurationbonus.py +++ b/eos/effects/cynosuraldurationbonus.py @@ -1,10 +1,10 @@ # cynosuralDurationBonus # # Used by: -# Ships from group: Force Recon Ship (7 of 8) +# Ships from group: Force Recon Ship (8 of 9) type = "passive" def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Cynosural Field", + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Cynosural Field Generator", "duration", ship.getModifiedItemAttr("durationBonus")) diff --git a/eos/effects/cynosuralgeneration.py b/eos/effects/cynosuralgeneration.py index b99325c0f..36e29c52d 100644 --- a/eos/effects/cynosuralgeneration.py +++ b/eos/effects/cynosuralgeneration.py @@ -1,7 +1,7 @@ # cynosuralGeneration # # Used by: -# Modules from group: Cynosural Field (2 of 2) +# Modules from group: Cynosural Field Generator (2 of 2) type = "active" diff --git a/eos/effects/cynosuraltheoryconsumptionbonus.py b/eos/effects/cynosuraltheoryconsumptionbonus.py index 217374cee..d6bda31b7 100644 --- a/eos/effects/cynosuraltheoryconsumptionbonus.py +++ b/eos/effects/cynosuraltheoryconsumptionbonus.py @@ -1,13 +1,13 @@ # cynosuralTheoryConsumptionBonus # # Used by: -# Ships from group: Force Recon Ship (7 of 8) +# Ships from group: Force Recon Ship (8 of 9) # Skill: Cynosural Field Theory type = "passive" def handler(fit, container, context): level = container.level if "skill" in context else 1 - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Cynosural Field", + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Cynosural Field Generator", "consumptionQuantity", container.getModifiedItemAttr("consumptionQuantityBonusPercentage") * level) diff --git a/eos/effects/damagecontrol.py b/eos/effects/damagecontrol.py index 05589e15a..f563fac03 100644 --- a/eos/effects/damagecontrol.py +++ b/eos/effects/damagecontrol.py @@ -11,6 +11,5 @@ def handler(fit, module, context): bonus = "%s%sDamageResonance" % (attrPrefix, damageType) bonus = "%s%s" % (bonus[0].lower(), bonus[1:]) booster = "%s%sDamageResonance" % (layer, damageType) - penalize = False if layer == 'hull' else True fit.ship.multiplyItemAttr(bonus, module.getModifiedItemAttr(booster), - stackingPenalties=penalize, penaltyGroup="preMul") + stackingPenalties=True, penaltyGroup="preMul") diff --git a/eos/effects/decreasetargetspeed.py b/eos/effects/decreasetargetspeed.py deleted file mode 100644 index dd482175d..000000000 --- a/eos/effects/decreasetargetspeed.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "active", "projected" - - -def handler(fit, module, context): - if "projected" not in context: - return - fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), - stackingPenalties=True) diff --git a/eos/effects/disintegratorweapondamagemultiply.py b/eos/effects/disintegratorweapondamagemultiply.py new file mode 100644 index 000000000..b070c9ee2 --- /dev/null +++ b/eos/effects/disintegratorweapondamagemultiply.py @@ -0,0 +1,11 @@ +# disintegratorWeaponDamageMultiply +# +# Used by: +# Modules from group: Entropic Radiation Sink (4 of 4) +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == "Precursor Weapon", + "damageMultiplier", module.getModifiedItemAttr("damageMultiplier"), + stackingPenalties=True) diff --git a/eos/effects/disintegratorweaponspeedmultiply.py b/eos/effects/disintegratorweaponspeedmultiply.py new file mode 100644 index 000000000..eccf38371 --- /dev/null +++ b/eos/effects/disintegratorweaponspeedmultiply.py @@ -0,0 +1,11 @@ +# disintegratorWeaponSpeedMultiply +# +# Used by: +# Modules from group: Entropic Radiation Sink (4 of 4) +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == "Precursor Weapon", + "speed", module.getModifiedItemAttr("speedMultiplier"), + stackingPenalties=True) diff --git a/eos/effects/dohacking.py b/eos/effects/dohacking.py index 233d29404..ddd2a7205 100644 --- a/eos/effects/dohacking.py +++ b/eos/effects/dohacking.py @@ -1,7 +1,7 @@ # doHacking # # Used by: -# Modules from group: Data Miners (9 of 9) +# Modules from group: Data Miners (10 of 10) type = "active" diff --git a/eos/effects/doomsdayaoebubble.py b/eos/effects/doomsdayaoebubble.py index 1eb899ac0..8d79e289d 100644 --- a/eos/effects/doomsdayaoebubble.py +++ b/eos/effects/doomsdayaoebubble.py @@ -2,6 +2,7 @@ # # Used by: # Module: Warp Disruption Burst Projector +# Structure Module: Standup Warp Disruption Burst Projector type = "projected", "active" diff --git a/eos/effects/doomsdayaoedamp.py b/eos/effects/doomsdayaoedamp.py index f943c9153..ab55bf185 100644 --- a/eos/effects/doomsdayaoedamp.py +++ b/eos/effects/doomsdayaoedamp.py @@ -2,6 +2,7 @@ # # Used by: # Module: Sensor Dampening Burst Projector +# Structure Module: Standup Sensor Dampening Burst Projector type = "projected", "active" diff --git a/eos/effects/doomsdayaoeecm.py b/eos/effects/doomsdayaoeecm.py index 230eb2f16..93d3dd847 100644 --- a/eos/effects/doomsdayaoeecm.py +++ b/eos/effects/doomsdayaoeecm.py @@ -2,6 +2,7 @@ # # Used by: # Module: ECM Jammer Burst Projector +# Structure Module: Standup ECM Jammer Burst Projector from eos.modifiedAttributeDict import ModifiedAttributeDict type = "projected", "active" diff --git a/eos/effects/doomsdayaoeneut.py b/eos/effects/doomsdayaoeneut.py index c132a470a..1be85f6bd 100644 --- a/eos/effects/doomsdayaoeneut.py +++ b/eos/effects/doomsdayaoeneut.py @@ -2,6 +2,7 @@ # # Used by: # Module: Energy Neutralization Burst Projector +# Structure Module: Standup Energy Neutralization Burst Projector from eos.saveddata.module import State from eos.modifiedAttributeDict import ModifiedAttributeDict diff --git a/eos/effects/doomsdayaoepaint.py b/eos/effects/doomsdayaoepaint.py index b02f68e38..1c039c23c 100644 --- a/eos/effects/doomsdayaoepaint.py +++ b/eos/effects/doomsdayaoepaint.py @@ -2,6 +2,7 @@ # # Used by: # Module: Target Illumination Burst Projector +# Structure Module: Standup Target Illumination Burst Projector type = "projected", "active" diff --git a/eos/effects/doomsdayaoetrack.py b/eos/effects/doomsdayaoetrack.py index 910ec7ae5..5ee1f31c9 100644 --- a/eos/effects/doomsdayaoetrack.py +++ b/eos/effects/doomsdayaoetrack.py @@ -2,6 +2,7 @@ # # Used by: # Module: Weapon Disruption Burst Projector +# Structure Module: Standup Weapon Disruption Burst Projector type = "active", "projected" diff --git a/eos/effects/doomsdayaoeweb.py b/eos/effects/doomsdayaoeweb.py index 913288b93..eb143b14b 100644 --- a/eos/effects/doomsdayaoeweb.py +++ b/eos/effects/doomsdayaoeweb.py @@ -2,6 +2,7 @@ # # Used by: # Module: Stasis Webification Burst Projector +# Structure Module: Standup Stasis Webification Burst Projector type = "active", "projected" diff --git a/eos/effects/dronearmordamagebonuseffect.py b/eos/effects/dronearmordamagebonuseffect.py index 144ad59d4..a5cfb52fe 100644 --- a/eos/effects/dronearmordamagebonuseffect.py +++ b/eos/effects/dronearmordamagebonuseffect.py @@ -1,7 +1,7 @@ # droneArmorDamageBonusEffect # # Used by: -# Ships from group: Logistics (5 of 6) +# Ships from group: Logistics (6 of 7) # Ship: Exequror # Ship: Scythe type = "passive" diff --git a/eos/effects/dronedamagebonusrequringdrones.py b/eos/effects/dronedamagebonusrequringdrones.py deleted file mode 100644 index 5f3e13ea1..000000000 --- a/eos/effects/dronedamagebonusrequringdrones.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, skill, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "damageMultiplier", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level) diff --git a/eos/effects/dronehullrepairbonuseffect.py b/eos/effects/dronehullrepairbonuseffect.py index e12dbbd2e..532f3e28e 100644 --- a/eos/effects/dronehullrepairbonuseffect.py +++ b/eos/effects/dronehullrepairbonuseffect.py @@ -1,7 +1,7 @@ # droneHullRepairBonusEffect # # Used by: -# Ships from group: Logistics (5 of 6) +# Ships from group: Logistics (6 of 7) # Ship: Exequror # Ship: Scythe type = "passive" diff --git a/eos/effects/dronemaxvelocitybonus.py b/eos/effects/dronemaxvelocitybonus.py index 938b34fa0..9ab5f4af0 100644 --- a/eos/effects/dronemaxvelocitybonus.py +++ b/eos/effects/dronemaxvelocitybonus.py @@ -2,10 +2,12 @@ # # Used by: # Modules named like: Drone Speed Augmentor (6 of 8) +# Implant: Overmind 'Goliath' Drone Tuner T25-10S +# Implant: Overmind 'Hawkmoth' Drone Tuner S10-25T type = "passive" def handler(fit, container, context): level = container.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "maxVelocity", container.getModifiedItemAttr("droneMaxVelocityBonus") * level) + "maxVelocity", container.getModifiedItemAttr("droneMaxVelocityBonus") * level, stackingPenalties=True) diff --git a/eos/effects/droneshieldbonusbonuseffect.py b/eos/effects/droneshieldbonusbonuseffect.py index 1c83ed29d..2f2d1e6c5 100644 --- a/eos/effects/droneshieldbonusbonuseffect.py +++ b/eos/effects/droneshieldbonusbonuseffect.py @@ -1,7 +1,7 @@ # droneShieldBonusBonusEffect # # Used by: -# Ships from group: Logistics (5 of 6) +# Ships from group: Logistics (6 of 7) # Ship: Exequror # Ship: Scythe type = "passive" diff --git a/eos/effects/elitebonuscommanddestroyerinfohidden1.py b/eos/effects/elitebonuscommanddestroyerinfohidden1.py deleted file mode 100644 index cf31b2b1b..000000000 --- a/eos/effects/elitebonuscommanddestroyerinfohidden1.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", - src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), - skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyermwdsigradius3.py b/eos/effects/elitebonuscommanddestroyermwdsigradius3.py deleted file mode 100644 index f9c1fded2..000000000 --- a/eos/effects/elitebonuscommanddestroyermwdsigradius3.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), "signatureRadiusBonus", - src.getModifiedItemAttr("eliteBonusCommandDestroyer3"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommandshipinformationhiddencs3.py b/eos/effects/elitebonuscommandshipinformationhiddencs3.py deleted file mode 100644 index eeee692ed..000000000 --- a/eos/effects/elitebonuscommandshipinformationhiddencs3.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", module.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscoveropsscanprobestrength2.py b/eos/effects/elitebonuscoveropsscanprobestrength2.py index 8b922cee1..77feea510 100644 --- a/eos/effects/elitebonuscoveropsscanprobestrength2.py +++ b/eos/effects/elitebonuscoveropsscanprobestrength2.py @@ -1,7 +1,7 @@ # eliteBonusCoverOpsScanProbeStrength2 # # Used by: -# Ships from group: Covert Ops (7 of 7) +# Ships from group: Covert Ops (8 of 8) type = "passive" diff --git a/eos/effects/elitebonuscovertops3pctdamagepercycle.py b/eos/effects/elitebonuscovertops3pctdamagepercycle.py new file mode 100644 index 000000000..550cdc94b --- /dev/null +++ b/eos/effects/elitebonuscovertops3pctdamagepercycle.py @@ -0,0 +1,10 @@ +# eliteBonusCovertOps3PCTdamagePerCycle +# +# Used by: +# Ship: Hydra +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), "damageMultiplierBonusPerCycle", + src.getModifiedItemAttr("eliteBonusCovertOps3"), skill="Covert Ops") diff --git a/eos/effects/elitebonusgunshipdronecapacity2.py b/eos/effects/elitebonusgunshipdronecapacity2.py deleted file mode 100644 index 61cf46c51..000000000 --- a/eos/effects/elitebonusgunshipdronecapacity2.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.ship.increaseItemAttr("droneCapacity", ship.getModifiedItemAttr("eliteBonusGunship2"), skill="Assault Frigates") diff --git a/eos/effects/elitebonusgunshipdronetracking2.py b/eos/effects/elitebonusgunshipdronetracking2.py deleted file mode 100644 index 10d8c753d..000000000 --- a/eos/effects/elitebonusgunshipdronetracking2.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "trackingSpeed", - src.getModifiedItemAttr("eliteBonusGunship2"), stackingPenalties=True, skill="Assault Frigates") diff --git a/eos/effects/elitebonusgunshipprojectiledamage2.py b/eos/effects/elitebonusgunshipprojectiledamage2.py deleted file mode 100644 index 81ad55805..000000000 --- a/eos/effects/elitebonusgunshipprojectiledamage2.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Projectile Turret"), - "damageMultiplier", ship.getModifiedItemAttr("eliteBonusGunship2"), - skill="Assault Frigates") diff --git a/eos/effects/elitebonusgunshipprojectileoptimal1.py b/eos/effects/elitebonusgunshipprojectileoptimal1.py deleted file mode 100644 index 75327d04f..000000000 --- a/eos/effects/elitebonusgunshipprojectileoptimal1.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Projectile Turret"), - "maxRange", ship.getModifiedItemAttr("eliteBonusGunship1"), skill="Assault Frigates") diff --git a/eos/effects/elitebonusreconscanprobestrength2.py b/eos/effects/elitebonusreconscanprobestrength2.py new file mode 100644 index 000000000..55db46631 --- /dev/null +++ b/eos/effects/elitebonusreconscanprobestrength2.py @@ -0,0 +1,10 @@ +# eliteBonusReconScanProbeStrength2 +# +# Used by: +# Ship: Tiamat +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Astrometrics"), "baseSensorStrength", + src.getModifiedItemAttr("eliteBonusReconShip2"), skill="Recon Ships") diff --git a/eos/effects/elitebonusreconship3pctdamagepercycle.py b/eos/effects/elitebonusreconship3pctdamagepercycle.py new file mode 100644 index 000000000..5aabfdcb7 --- /dev/null +++ b/eos/effects/elitebonusreconship3pctdamagepercycle.py @@ -0,0 +1,10 @@ +# eliteBonusReconShip3PCTdamagePerCycle +# +# Used by: +# Ship: Tiamat +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), "damageMultiplierBonusPerCycle", + src.getModifiedItemAttr("eliteBonusReconShip3"), skill="Recon Ships") diff --git a/eos/effects/emergencyhullenergizer.py b/eos/effects/emergencyhullenergizer.py new file mode 100644 index 000000000..c2de4f4f5 --- /dev/null +++ b/eos/effects/emergencyhullenergizer.py @@ -0,0 +1,13 @@ +# emergencyHullEnergizer +# +# Used by: +# Variations of module: Capital Emergency Hull Energizer I (5 of 5) +type = "active" +runtime = "late" + + +def handler(fit, src, context): + for dmgType in ('em', 'thermal', 'kinetic', 'explosive'): + fit.ship.multiplyItemAttr('{}DamageResonance'.format(dmgType), + src.getModifiedItemAttr("hull{}DamageResonance".format(dmgType.title())), + stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/energydestabilizationnew.py b/eos/effects/energydestabilizationnew.py deleted file mode 100644 index 7a45fe8b7..000000000 --- a/eos/effects/energydestabilizationnew.py +++ /dev/null @@ -1,12 +0,0 @@ -# Not used by any item -from eos.saveddata.module import State - -type = "active", "projected" - - -def handler(fit, src, context): - if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): - multiplier = src.amountActive if hasattr(src, "amountActive") else 1 - amount = src.getModifiedItemAttr("energyNeutralizerAmount") - time = src.getModifiedItemAttr("duration") - fit.addDrain(src, time, amount * multiplier, 0) diff --git a/eos/effects/energyneutralizerfalloff.py b/eos/effects/energyneutralizerfalloff.py index 37d829baa..67e55e7ad 100644 --- a/eos/effects/energyneutralizerfalloff.py +++ b/eos/effects/energyneutralizerfalloff.py @@ -1,7 +1,7 @@ # energyNeutralizerFalloff # # Used by: -# Modules from group: Energy Neutralizer (51 of 51) +# Modules from group: Energy Neutralizer (54 of 54) from eos.saveddata.module import State from eos.modifiedAttributeDict import ModifiedAttributeDict diff --git a/eos/effects/energynosferatufalloff.py b/eos/effects/energynosferatufalloff.py index 0dc4fd81b..2d6414e61 100644 --- a/eos/effects/energynosferatufalloff.py +++ b/eos/effects/energynosferatufalloff.py @@ -1,7 +1,7 @@ # energyNosferatuFalloff # # Used by: -# Modules from group: Energy Nosferatu (51 of 51) +# Modules from group: Energy Nosferatu (54 of 54) from eos.modifiedAttributeDict import ModifiedAttributeDict type = "active", "projected" diff --git a/eos/effects/energytransfer.py b/eos/effects/energytransfer.py deleted file mode 100644 index 4a5ef6530..000000000 --- a/eos/effects/energytransfer.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "projected", "active" - - -def handler(fit, src, context): - if "projected" in context: - amount = src.getModifiedItemAttr("powerTransferAmount") - duration = src.getModifiedItemAttr("duration") - fit.addDrain(src, duration, -amount, 0) diff --git a/eos/effects/energyweapondamagemultiply.py b/eos/effects/energyweapondamagemultiply.py index 0eb10ef75..42dddd186 100644 --- a/eos/effects/energyweapondamagemultiply.py +++ b/eos/effects/energyweapondamagemultiply.py @@ -1,7 +1,7 @@ # energyWeaponDamageMultiply # # Used by: -# Modules from group: Heat Sink (18 of 18) +# Modules from group: Heat Sink (19 of 19) type = "passive" diff --git a/eos/effects/energyweaponspeedmultiply.py b/eos/effects/energyweaponspeedmultiply.py index 209787284..19dacfb85 100644 --- a/eos/effects/energyweaponspeedmultiply.py +++ b/eos/effects/energyweaponspeedmultiply.py @@ -1,7 +1,7 @@ # energyWeaponSpeedMultiply # # Used by: -# Modules from group: Heat Sink (18 of 18) +# Modules from group: Heat Sink (19 of 19) type = "passive" diff --git a/eos/effects/entosislink.py b/eos/effects/entosislink.py index a28b8da2d..11b5faa28 100644 --- a/eos/effects/entosislink.py +++ b/eos/effects/entosislink.py @@ -7,3 +7,9 @@ type = "active" def handler(fit, module, context): fit.ship.forceItemAttr("disallowAssistance", module.getModifiedItemAttr("disallowAssistance")) + for scanType in ("Gravimetric", "Magnetometric", "Radar", "Ladar"): + fit.ship.boostItemAttr( + "scan{}Strength".format(scanType), + module.getModifiedItemAttr("scan{}StrengthPercent".format(scanType)), + stackingPenalties=True + ) diff --git a/eos/effects/ewtargetpaint.py b/eos/effects/ewtargetpaint.py deleted file mode 100644 index c61adeb8b..000000000 --- a/eos/effects/ewtargetpaint.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "projected", "active" - - -def handler(fit, container, context): - if "projected" in context: - fit.ship.boostItemAttr("signatureRadius", container.getModifiedItemAttr("signatureRadiusBonus"), - stackingPenalties=True) diff --git a/eos/effects/fighterabilityattackm.py b/eos/effects/fighterabilityattackm.py index 2ea002819..d0a028f48 100644 --- a/eos/effects/fighterabilityattackm.py +++ b/eos/effects/fighterabilityattackm.py @@ -1,4 +1,8 @@ -# Not used by any item +# fighterAbilityAttackM +# +# Used by: +# Items from category: Fighter (50 of 82) +# Fighters from group: Heavy Fighter (34 of 34) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilityecm.py b/eos/effects/fighterabilityecm.py index e821c748a..cfc9c21db 100644 --- a/eos/effects/fighterabilityecm.py +++ b/eos/effects/fighterabilityecm.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityECM +# +# Used by: +# Fighters named like: Scarab (4 of 4) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilityenergyneutralizer.py b/eos/effects/fighterabilityenergyneutralizer.py index 8a43dff19..28b68d089 100644 --- a/eos/effects/fighterabilityenergyneutralizer.py +++ b/eos/effects/fighterabilityenergyneutralizer.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityEnergyNeutralizer +# +# Used by: +# Fighters named like: Cenobite (4 of 4) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilityevasivemaneuvers.py b/eos/effects/fighterabilityevasivemaneuvers.py index bf17edc92..151effaaa 100644 --- a/eos/effects/fighterabilityevasivemaneuvers.py +++ b/eos/effects/fighterabilityevasivemaneuvers.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityEvasiveManeuvers +# +# Used by: +# Fighters from group: Light Fighter (16 of 32) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilitylaunchbomb.py b/eos/effects/fighterabilitylaunchbomb.py index bd46513bf..03763fd5a 100644 --- a/eos/effects/fighterabilitylaunchbomb.py +++ b/eos/effects/fighterabilitylaunchbomb.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityLaunchBomb +# +# Used by: +# Fighters from group: Heavy Fighter (16 of 34) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilitymicrowarpdrive.py b/eos/effects/fighterabilitymicrowarpdrive.py index 6dfbae73a..d9cfdbe47 100644 --- a/eos/effects/fighterabilitymicrowarpdrive.py +++ b/eos/effects/fighterabilitymicrowarpdrive.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityMicroWarpDrive +# +# Used by: +# Items from category: Fighter (48 of 82) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilitymissiles.py b/eos/effects/fighterabilitymissiles.py index 8dc752ee2..c6dde777e 100644 --- a/eos/effects/fighterabilitymissiles.py +++ b/eos/effects/fighterabilitymissiles.py @@ -1,4 +1,8 @@ -# Not used by any item +# fighterAbilityMissiles +# +# Used by: +# Items from category: Fighter (48 of 82) +# Fighters from group: Light Fighter (32 of 32) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilitystasiswebifier.py b/eos/effects/fighterabilitystasiswebifier.py index 1fd6dd96f..640cff4c0 100644 --- a/eos/effects/fighterabilitystasiswebifier.py +++ b/eos/effects/fighterabilitystasiswebifier.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityStasisWebifier +# +# Used by: +# Fighters named like: Dromi (4 of 4) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/fighterabilitywarpdisruption.py b/eos/effects/fighterabilitywarpdisruption.py index d362fb33f..e62c34de7 100644 --- a/eos/effects/fighterabilitywarpdisruption.py +++ b/eos/effects/fighterabilitywarpdisruption.py @@ -1,4 +1,7 @@ -# Not used by any item +# fighterAbilityWarpDisruption +# +# Used by: +# Fighters named like: Siren (4 of 4) """ Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the effects, and thus this effect file contains some custom information useful only to fighters. diff --git a/eos/effects/flagshipmultirelayeffect.py b/eos/effects/flagshipmultirelayeffect.py deleted file mode 100644 index 20bf757c2..000000000 --- a/eos/effects/flagshipmultirelayeffect.py +++ /dev/null @@ -1,11 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - # Note: we increase maxGroupActive by two. - # If we only increased it by one, we'd get the number to stay equal - # As Comman Processors use one themselves too - fit.modules.filteredItemIncrease(lambda mod: mod.item.group.name == "Gang Coordinator" and - "maxGroupActive" in mod.itemModifiedAttributes, - "maxGroupActive", 1) diff --git a/eos/effects/fueledarmorrepair.py b/eos/effects/fueledarmorrepair.py index 875a25cd2..79f9cf1e2 100644 --- a/eos/effects/fueledarmorrepair.py +++ b/eos/effects/fueledarmorrepair.py @@ -1,7 +1,7 @@ # fueledArmorRepair # # Used by: -# Modules from group: Ancillary Armor Repairer (4 of 4) +# Modules from group: Ancillary Armor Repairer (7 of 7) runTime = "late" type = "active" @@ -14,4 +14,7 @@ def handler(fit, module, context): amount = module.getModifiedItemAttr("armorDamageAmount") * multiplier speed = module.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", amount / speed) + rps = amount / speed + fit.extraAttributes.increase("armorRepair", rps) + fit.extraAttributes.increase("armorRepairPreSpool", rps) + fit.extraAttributes.increase("armorRepairFullSpool", rps) diff --git a/eos/effects/fueledshieldboosting.py b/eos/effects/fueledshieldboosting.py index 36db95e2c..0cbbd439c 100644 --- a/eos/effects/fueledshieldboosting.py +++ b/eos/effects/fueledshieldboosting.py @@ -1,7 +1,7 @@ # fueledShieldBoosting # # Used by: -# Modules from group: Ancillary Shield Booster (5 of 5) +# Modules from group: Ancillary Shield Booster (8 of 8) runTime = "late" type = "active" diff --git a/eos/effects/hybridweapondamagemultiply.py b/eos/effects/hybridweapondamagemultiply.py index 0fe01369c..87166fc7c 100644 --- a/eos/effects/hybridweapondamagemultiply.py +++ b/eos/effects/hybridweapondamagemultiply.py @@ -1,7 +1,7 @@ # hybridWeaponDamageMultiply # # Used by: -# Modules from group: Magnetic Field Stabilizer (14 of 14) +# Modules from group: Magnetic Field Stabilizer (15 of 15) type = "passive" diff --git a/eos/effects/hybridweaponspeedmultiply.py b/eos/effects/hybridweaponspeedmultiply.py index ac601b221..876ef5b3c 100644 --- a/eos/effects/hybridweaponspeedmultiply.py +++ b/eos/effects/hybridweaponspeedmultiply.py @@ -1,7 +1,7 @@ # hybridWeaponSpeedMultiply # # Used by: -# Modules from group: Magnetic Field Stabilizer (14 of 14) +# Modules from group: Magnetic Field Stabilizer (15 of 15) type = "passive" diff --git a/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py b/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py index 19d58e81e..500775345 100644 --- a/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py +++ b/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py @@ -2,6 +2,7 @@ # # Used by: # Variations of module: Ice Harvester Upgrade I (5 of 5) +# Module: Frostline 'Omnivore' Harvester Upgrade type = "passive" diff --git a/eos/effects/iceharvestercapacitorneedmultiplier.py b/eos/effects/iceharvestercapacitorneedmultiplier.py deleted file mode 100644 index 93338c76f..000000000 --- a/eos/effects/iceharvestercapacitorneedmultiplier.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Ice Harvesting"), - "capacitorNeed", ship.getModifiedItemAttr("iceHarvestCycleBonus")) diff --git a/eos/effects/implantwarpscramblerangebonus.py b/eos/effects/implantwarpscramblerangebonus.py new file mode 100644 index 000000000..8eea16959 --- /dev/null +++ b/eos/effects/implantwarpscramblerangebonus.py @@ -0,0 +1,10 @@ +# implantWarpScrambleRangeBonus +# +# Used by: +# Implants named like: Inquest 'Hedone' Entanglement Optimizer WS (3 of 3) +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Warp Scrambler", "maxRange", + src.getModifiedItemAttr("warpScrambleRangeBonus"), stackingPenalties=False) diff --git a/eos/effects/increasesignatureradiusonline.py b/eos/effects/increasesignatureradiusonline.py index bc58e44b9..1ae8fe69a 100644 --- a/eos/effects/increasesignatureradiusonline.py +++ b/eos/effects/increasesignatureradiusonline.py @@ -6,4 +6,4 @@ type = "passive" def handler(fit, module, context): - fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus")) + fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"), stackingPenalties=True) diff --git a/eos/effects/informationwarfaremaxtargetrangebonus.py b/eos/effects/informationwarfaremaxtargetrangebonus.py deleted file mode 100644 index dfb0a01fc..000000000 --- a/eos/effects/informationwarfaremaxtargetrangebonus.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "gang" -gangBoost = "maxTargetRange" -gangBonus = "maxTargetRangeBonus" - - -def handler(fit, container, context): - fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/informationwarfaremindlinkhidden.py b/eos/effects/informationwarfaremindlinkhidden.py deleted file mode 100644 index 014ad1d31..000000000 --- a/eos/effects/informationwarfaremindlinkhidden.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, implant, context): - fit.character.getSkill("Information Command").suppress() - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", implant.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/largedisintegratorskilldmgbonus.py b/eos/effects/largedisintegratorskilldmgbonus.py new file mode 100644 index 000000000..c3b03337d --- /dev/null +++ b/eos/effects/largedisintegratorskilldmgbonus.py @@ -0,0 +1,11 @@ +# largeDisintegratorSkillDmgBonus +# +# Used by: +# Skill: Large Disintegrator Specialization +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Disintegrator Specialization"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/largeprecursorturretdmgbonusrequiredskill.py b/eos/effects/largeprecursorturretdmgbonusrequiredskill.py new file mode 100644 index 000000000..32ba9bb8b --- /dev/null +++ b/eos/effects/largeprecursorturretdmgbonusrequiredskill.py @@ -0,0 +1,11 @@ +# largePrecursorTurretDmgBonusRequiredSkill +# +# Used by: +# Skill: Large Precursor Weapon +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Precursor Weapon"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/lightningweapon.py b/eos/effects/lightningweapon.py index 4f2324035..81987466e 100644 --- a/eos/effects/lightningweapon.py +++ b/eos/effects/lightningweapon.py @@ -1,4 +1,7 @@ -# Not used by any item +# lightningWeapon +# +# Used by: +# Structure Module: Standup Arcing Vorton Projector I type = 'active' diff --git a/eos/effects/maraudermodeeffect26.py b/eos/effects/maraudermodeeffect26.py deleted file mode 100644 index f892a5a52..000000000 --- a/eos/effects/maraudermodeeffect26.py +++ /dev/null @@ -1,52 +0,0 @@ -# Not used by any item -type = "active" -runTime = "early" - - -def handler(fit, module, context): - # Resistances - for layer, attrPrefix in (('shield', 'shield'), ('armor', 'armor'), ('hull', '')): - for damageType in ('Kinetic', 'Thermal', 'Explosive', 'Em'): - bonus = "%s%sDamageResonance" % (attrPrefix, damageType) - bonus = "%s%s" % (bonus[0].lower(), bonus[1:]) - booster = "%s%sDamageResonance" % (layer, damageType) - penalize = False if layer == 'hull' else True - fit.ship.multiplyItemAttr(bonus, module.getModifiedItemAttr(booster), - stackingPenalties=penalize, penaltyGroup="preMul") - - # Turrets - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Energy Turret") or - mod.item.requiresSkill("Large Hybrid Turret") or - mod.item.requiresSkill("Large Projectile Turret"), - "maxRange", module.getModifiedItemAttr("maxRangeBonus"), - stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Energy Turret") or - mod.item.requiresSkill("Large Hybrid Turret") or - mod.item.requiresSkill("Large Projectile Turret"), - "falloff", module.getModifiedItemAttr("falloffBonus"), - stackingPenalties=True) - - # Missiles - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes") or - mod.charge.requiresSkill("Cruise Missiles") or - mod.charge.requiresSkill("Heavy Missiles"), - "maxVelocity", module.getModifiedItemAttr("missileVelocityBonus")) - - # Tanking - fit.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Capital Repair Systems") or mod.item.requiresSkill("Repair Systems"), - "armorDamageAmount", module.getModifiedItemAttr("armorDamageAmountBonus"), - stackingPenalties=True) - fit.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Capital Shield Operation") or mod.item.requiresSkill("Shield Operation"), - "shieldBonus", module.getModifiedItemAttr("shieldBoostMultiplier"), - stackingPenalties=True) - - # Speed penalty - fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor")) - - # Max locked targets - fit.ship.forceItemAttr("maxLockedTargets", module.getModifiedItemAttr("maxLockedTargets")) - - # Block Hostile ewar - fit.ship.forceItemAttr("disallowOffensiveModifiers", module.getModifiedItemAttr("disallowOffensiveModifiers")) diff --git a/eos/effects/massaddpassive.py b/eos/effects/massaddpassive.py deleted file mode 100644 index 68847c8ec..000000000 --- a/eos/effects/massaddpassive.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.increaseItemAttr("mass", module.getModifiedItemAttr("mass") or 0) diff --git a/eos/effects/massentanglereffect5.py b/eos/effects/massentanglereffect5.py new file mode 100644 index 000000000..ebeb25f5a --- /dev/null +++ b/eos/effects/massentanglereffect5.py @@ -0,0 +1,17 @@ +# massEntanglerEffect5 +# +# Used by: +# Module: Zero-Point Mass Entangler +type = "active" + + +def handler(fit, src, context): + fit.ship.increaseItemAttr("warpScrambleStatus", src.getModifiedItemAttr("warpScrambleStrength")) + fit.ship.boostItemAttr("mass", src.getModifiedItemAttr("massBonusPercentage"), stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner"), "speedFactor", + src.getModifiedItemAttr("speedFactorBonus"), stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner"), "speedBoostFactor", + src.getModifiedItemAttr("speedBoostFactorBonus")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), "activationBlocked", + src.getModifiedItemAttr("activationBlockedStrenght")) + fit.ship.boostItemAttr("maxVelocity", src.getModifiedItemAttr("maxVelocityBonus"), stackingPenalties=True) diff --git a/eos/effects/maxvelocityaddpassive.py b/eos/effects/maxvelocityaddpassive.py deleted file mode 100644 index 33d05f88c..000000000 --- a/eos/effects/maxvelocityaddpassive.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.increaseItemAttr("maxVelocity", module.getModifiedItemAttr("maxVelocity")) diff --git a/eos/effects/mediumdisintegratorskilldmgbonus.py b/eos/effects/mediumdisintegratorskilldmgbonus.py new file mode 100644 index 000000000..f7a2cc10e --- /dev/null +++ b/eos/effects/mediumdisintegratorskilldmgbonus.py @@ -0,0 +1,11 @@ +# mediumDisintegratorSkillDmgBonus +# +# Used by: +# Skill: Medium Disintegrator Specialization +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Disintegrator Specialization"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/mediumprecursorturretdmgbonusrequiredskill.py b/eos/effects/mediumprecursorturretdmgbonusrequiredskill.py new file mode 100644 index 000000000..c35fcda31 --- /dev/null +++ b/eos/effects/mediumprecursorturretdmgbonusrequiredskill.py @@ -0,0 +1,11 @@ +# mediumPrecursorTurretDmgBonusRequiredSkill +# +# Used by: +# Skill: Medium Precursor Weapon +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/microjumpportaldrive.py b/eos/effects/microjumpportaldrive.py index 1e5a02569..0b96a9743 100644 --- a/eos/effects/microjumpportaldrive.py +++ b/eos/effects/microjumpportaldrive.py @@ -5,5 +5,6 @@ type = "active" -def handler(fit, src, context): - pass +def handler(fit, module, context): + fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonusPercent"), + stackingPenalties=True) diff --git a/eos/effects/minercpuusagemultiplypercent2.py b/eos/effects/minercpuusagemultiplypercent2.py index 7b8e6025a..7cba56603 100644 --- a/eos/effects/minercpuusagemultiplypercent2.py +++ b/eos/effects/minercpuusagemultiplypercent2.py @@ -2,6 +2,7 @@ # # Used by: # Variations of module: Mining Laser Upgrade I (5 of 5) +# Module: Frostline 'Omnivore' Harvester Upgrade type = "passive" diff --git a/eos/effects/minigamevirusstrengthbonus.py b/eos/effects/minigamevirusstrengthbonus.py index 2023d0407..0e60325db 100644 --- a/eos/effects/minigamevirusstrengthbonus.py +++ b/eos/effects/minigamevirusstrengthbonus.py @@ -1,7 +1,7 @@ # minigameVirusStrengthBonus # # Used by: -# Ships from group: Covert Ops (7 of 7) +# Ships from group: Covert Ops (7 of 8) # Ships named like: Stratios (2 of 2) # Subsystems named like: Defensive Covert Reconfiguration (4 of 4) # Ship: Astero diff --git a/eos/effects/miningdirectorbonuscommandbonuseffective.py b/eos/effects/miningdirectorbonuscommandbonuseffective.py deleted file mode 100644 index 43f712ff5..000000000 --- a/eos/effects/miningdirectorbonuscommandbonuseffective.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Director"), - "commandBonus", ship.getModifiedItemAttr("commandBonusEffective")) diff --git a/eos/effects/miningdurationmultiplieronline.py b/eos/effects/miningdurationmultiplieronline.py new file mode 100644 index 000000000..3d4cad5a5 --- /dev/null +++ b/eos/effects/miningdurationmultiplieronline.py @@ -0,0 +1,10 @@ +# miningDurationMultiplierOnline +# +# Used by: +# Module: Frostline 'Omnivore' Harvester Upgrade +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mining Laser", + "duration", module.getModifiedItemAttr("miningDurationMultiplier")) diff --git a/eos/effects/mininglaser.py b/eos/effects/mininglaser.py index 09bda59bf..1351132dd 100644 --- a/eos/effects/mininglaser.py +++ b/eos/effects/mininglaser.py @@ -4,6 +4,7 @@ # Modules from group: Frequency Mining Laser (3 of 3) # Modules from group: Mining Laser (15 of 15) # Modules from group: Strip Miner (5 of 5) +# Module: Citizen Miner type = 'active' diff --git a/eos/effects/miningyieldmultiplypercent.py b/eos/effects/miningyieldmultiplypercent.py index 281b7169f..a02b4333b 100644 --- a/eos/effects/miningyieldmultiplypercent.py +++ b/eos/effects/miningyieldmultiplypercent.py @@ -2,6 +2,7 @@ # # Used by: # Variations of module: Mining Laser Upgrade I (5 of 5) +# Module: Frostline 'Omnivore' Harvester Upgrade type = "passive" diff --git a/eos/effects/missileaoevelocitybonusonline.py b/eos/effects/missileaoevelocitybonusonline.py index e5d0d27c9..03ddd6715 100644 --- a/eos/effects/missileaoevelocitybonusonline.py +++ b/eos/effects/missileaoevelocitybonusonline.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Missile Guidance Enhancer (3 of 3) +# Module: ML-EKP 'Polybolos' Ballistic Control System type = "passive" diff --git a/eos/effects/missiledmgbonus.py b/eos/effects/missiledmgbonus.py index d565bdaaf..4fca60500 100644 --- a/eos/effects/missiledmgbonus.py +++ b/eos/effects/missiledmgbonus.py @@ -1,7 +1,7 @@ # missileDMGBonus # # Used by: -# Modules from group: Ballistic Control system (20 of 20) +# Modules from group: Ballistic Control system (22 of 22) type = "passive" diff --git a/eos/effects/missilelauncherspeedmultiplier.py b/eos/effects/missilelauncherspeedmultiplier.py index cc339b5ab..2655e614e 100644 --- a/eos/effects/missilelauncherspeedmultiplier.py +++ b/eos/effects/missilelauncherspeedmultiplier.py @@ -1,7 +1,7 @@ # missileLauncherSpeedMultiplier # # Used by: -# Modules from group: Ballistic Control system (20 of 20) +# Modules from group: Ballistic Control system (22 of 22) type = "passive" diff --git a/eos/effects/missileskillwarheadupgradesemdamagebonus.py b/eos/effects/missileskillwarheadupgradesemdamagebonus.py index 675b09ab0..63e95f67f 100644 --- a/eos/effects/missileskillwarheadupgradesemdamagebonus.py +++ b/eos/effects/missileskillwarheadupgradesemdamagebonus.py @@ -1,7 +1,7 @@ # missileSkillWarheadUpgradesEmDamageBonus # # Used by: -# Implants named like: Agency 'Pyrolancea' DB Dose (3 of 3) +# Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) # Skill: Warhead Upgrades type = "passive" diff --git a/eos/effects/missileskillwarheadupgradesexplosivedamagebonus.py b/eos/effects/missileskillwarheadupgradesexplosivedamagebonus.py index 98c7941d6..6a6228e8e 100644 --- a/eos/effects/missileskillwarheadupgradesexplosivedamagebonus.py +++ b/eos/effects/missileskillwarheadupgradesexplosivedamagebonus.py @@ -1,7 +1,7 @@ # missileSkillWarheadUpgradesExplosiveDamageBonus # # Used by: -# Implants named like: Agency 'Pyrolancea' DB Dose (3 of 3) +# Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) # Skill: Warhead Upgrades type = "passive" diff --git a/eos/effects/missileskillwarheadupgradeskineticdamagebonus.py b/eos/effects/missileskillwarheadupgradeskineticdamagebonus.py index 497f1e20c..8b25142a8 100644 --- a/eos/effects/missileskillwarheadupgradeskineticdamagebonus.py +++ b/eos/effects/missileskillwarheadupgradeskineticdamagebonus.py @@ -1,7 +1,7 @@ # missileSkillWarheadUpgradesKineticDamageBonus # # Used by: -# Implants named like: Agency 'Pyrolancea' DB Dose (3 of 3) +# Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) # Skill: Warhead Upgrades type = "passive" diff --git a/eos/effects/missileskillwarheadupgradesthermaldamagebonus.py b/eos/effects/missileskillwarheadupgradesthermaldamagebonus.py index 23685da0a..737e5d20d 100644 --- a/eos/effects/missileskillwarheadupgradesthermaldamagebonus.py +++ b/eos/effects/missileskillwarheadupgradesthermaldamagebonus.py @@ -1,7 +1,7 @@ # missileSkillWarheadUpgradesThermalDamageBonus # # Used by: -# Implants named like: Agency 'Pyrolancea' DB Dose (3 of 3) +# Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) # Skill: Warhead Upgrades type = "passive" diff --git a/eos/effects/modifyarmorresonancepassivepreassignment.py b/eos/effects/modifyarmorresonancepassivepreassignment.py deleted file mode 100644 index 467f80846..000000000 --- a/eos/effects/modifyarmorresonancepassivepreassignment.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.preAssignItemAttr("armor{0}DamageResonance".format(type), - module.getModifiedItemAttr("passiveArmor{0}DamageResonance".format(type))) diff --git a/eos/effects/modifyenergywarfareresistance.py b/eos/effects/modifyenergywarfareresistance.py index 8cffe1386..874cbf0d7 100644 --- a/eos/effects/modifyenergywarfareresistance.py +++ b/eos/effects/modifyenergywarfareresistance.py @@ -1,7 +1,7 @@ # modifyEnergyWarfareResistance # # Used by: -# Modules from group: Capacitor Battery (27 of 27) +# Modules from group: Capacitor Battery (30 of 30) type = "passive" diff --git a/eos/effects/modifyshieldresonancepassivepreassignment.py b/eos/effects/modifyshieldresonancepassivepreassignment.py deleted file mode 100644 index f61d559ce..000000000 --- a/eos/effects/modifyshieldresonancepassivepreassignment.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.preAssignItemAttr("shield{0}DamageResonance".format(type), - module.getModifiedItemAttr("passiveShield{0}DamageResonance".format(type))) diff --git a/eos/effects/modifyshipagilitypassivepreassignment.py b/eos/effects/modifyshipagilitypassivepreassignment.py deleted file mode 100644 index 4691fa106..000000000 --- a/eos/effects/modifyshipagilitypassivepreassignment.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -runTime = "early" -type = "passive" - - -def handler(fit, module, context): - fit.ship.preAssignItemAttr("agility", module.getModifiedItemAttr("agility")) diff --git a/eos/effects/modulebonusafterburner.py b/eos/effects/modulebonusafterburner.py index c26412bb0..71f4c96b1 100644 --- a/eos/effects/modulebonusafterburner.py +++ b/eos/effects/modulebonusafterburner.py @@ -1,7 +1,7 @@ # moduleBonusAfterburner # # Used by: -# Modules from group: Propulsion Module (62 of 127) +# Modules from group: Propulsion Module (65 of 133) type = "active" runTime = "late" diff --git a/eos/effects/modulebonusdronedamageamplifier.py b/eos/effects/modulebonusdronedamageamplifier.py index ac7fb062d..e3a87f990 100644 --- a/eos/effects/modulebonusdronedamageamplifier.py +++ b/eos/effects/modulebonusdronedamageamplifier.py @@ -3,6 +3,7 @@ # Used by: # Modules from group: Drone Damage Modules (11 of 11) # Modules named like: C3 'Hivaa Saitsuo' Ballistic Control System (2 of 2) +# Module: Abyssal Ballistic Control System type = "passive" diff --git a/eos/effects/modulebonusmicrowarpdrive.py b/eos/effects/modulebonusmicrowarpdrive.py index 75df915ce..70478fa71 100644 --- a/eos/effects/modulebonusmicrowarpdrive.py +++ b/eos/effects/modulebonusmicrowarpdrive.py @@ -1,7 +1,7 @@ # moduleBonusMicrowarpdrive # # Used by: -# Modules from group: Propulsion Module (65 of 127) +# Modules from group: Propulsion Module (68 of 133) type = "active" runTime = "late" diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index be5843988..c7cf29ce3 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -3,14 +3,14 @@ # Used by: # Variations of module: Armor Command Burst I (2 of 2) -''' +""" Some documentation: When the fit is calculated, we gather up all the gang effects and stick them onto the fit. We don't run the actual effect yet, only give the fit details so that it can run the effect at a later time. We need to do this so that we can only run the strongest effect. When we are done, one of the last things that we do with the fit is to loop through those bonuses and actually run the effect. To do this, we have a special argument passed into the effect handler that tells it which warfareBuffID to run (shouldn't need this right now, but better safe than sorry) -''' +""" type = "active", "gang" diff --git a/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py b/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py index bf8dd4894..6f68c7292 100644 --- a/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py +++ b/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py @@ -2,7 +2,7 @@ # # Used by: # Modules from group: Rig Anchor (4 of 4) -# Implants named like: Agency 'Overclocker' SB Dose (3 of 3) +# Implants named like: Agency 'Overclocker' SB Dose (4 of 4) # Implants named like: grade Snake (16 of 18) # Modules named like: Auxiliary Thrusters (8 of 8) # Implant: Quafe Zero diff --git a/eos/effects/noscpuneedbonuseffect.py b/eos/effects/noscpuneedbonuseffect.py deleted file mode 100644 index d54b6e023..000000000 --- a/eos/effects/noscpuneedbonuseffect.py +++ /dev/null @@ -1,10 +0,0 @@ -# nosCpuNeedBonusEffect -# -# Used by: -# Ship: Rabisu -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "cpu", - src.getModifiedItemAttr("nosferatuCpuNeedBonus")) diff --git a/eos/effects/npcentityremotearmorrepairer.py b/eos/effects/npcentityremotearmorrepairer.py index 689a1ac35..e74505c96 100644 --- a/eos/effects/npcentityremotearmorrepairer.py +++ b/eos/effects/npcentityremotearmorrepairer.py @@ -9,4 +9,7 @@ def handler(fit, container, context): if "projected" in context: bonus = container.getModifiedItemAttr("armorDamageAmount") duration = container.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", bonus / duration) + rps = bonus / duration + fit.extraAttributes.increase("armorRepair", rps) + fit.extraAttributes.increase("armorRepairPreSpool", rps) + fit.extraAttributes.increase("armorRepairFullSpool", rps) diff --git a/eos/effects/orecapitalshipshieldtransferfalloff.py b/eos/effects/orecapitalshipshieldtransferfalloff.py deleted file mode 100644 index 6ec5e26c5..000000000 --- a/eos/effects/orecapitalshipshieldtransferfalloff.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Shield Emission Systems"), - "falloffEffectiveness", src.getModifiedItemAttr("shipBonusORECapital3"), - skill="Capital Industrial Ships") diff --git a/eos/effects/orecapitalshipshieldtransferrange.py b/eos/effects/orecapitalshipshieldtransferrange.py deleted file mode 100644 index 2e7f24d88..000000000 --- a/eos/effects/orecapitalshipshieldtransferrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Shield Emission Systems"), - "maxRange", ship.getModifiedItemAttr("shipBonusORECapital3"), - skill="Capital Industrial Ships") diff --git a/eos/effects/overloadrofbonus.py b/eos/effects/overloadrofbonus.py index 4d5707173..c0fedef1a 100644 --- a/eos/effects/overloadrofbonus.py +++ b/eos/effects/overloadrofbonus.py @@ -2,7 +2,7 @@ # # Used by: # Modules from group: Missile Launcher Torpedo (22 of 22) -# Items from market group: Ship Equipment > Turrets & Bays (429 of 863) +# Items from market group: Ship Equipment > Turrets & Bays (429 of 883) # Module: Interdiction Sphere Launcher I type = "overheat" diff --git a/eos/effects/overloadselfarmordamageamountdurationbonus.py b/eos/effects/overloadselfarmordamageamountdurationbonus.py index 3545e6dab..c7a2a1049 100644 --- a/eos/effects/overloadselfarmordamageamountdurationbonus.py +++ b/eos/effects/overloadselfarmordamageamountdurationbonus.py @@ -1,8 +1,8 @@ # overloadSelfArmorDamageAmountDurationBonus # # Used by: -# Modules from group: Ancillary Armor Repairer (4 of 4) -# Modules from group: Armor Repair Unit (105 of 105) +# Modules from group: Ancillary Armor Repairer (7 of 7) +# Modules from group: Armor Repair Unit (108 of 108) type = "overheat" diff --git a/eos/effects/overloadselfdamagebonus.py b/eos/effects/overloadselfdamagebonus.py index a60a741e4..073b1ead3 100644 --- a/eos/effects/overloadselfdamagebonus.py +++ b/eos/effects/overloadselfdamagebonus.py @@ -3,6 +3,7 @@ # Used by: # Modules from group: Energy Weapon (101 of 214) # Modules from group: Hybrid Weapon (105 of 221) +# Modules from group: Precursor Weapon (15 of 15) # Modules from group: Projectile Weapon (99 of 165) type = "overheat" diff --git a/eos/effects/overloadselfdurationbonus.py b/eos/effects/overloadselfdurationbonus.py index 356eab98a..e4ecde55c 100644 --- a/eos/effects/overloadselfdurationbonus.py +++ b/eos/effects/overloadselfdurationbonus.py @@ -1,16 +1,17 @@ # overloadSelfDurationBonus # # Used by: +# Modules from group: Ancillary Remote Shield Booster (4 of 4) # Modules from group: Capacitor Booster (59 of 59) -# Modules from group: Energy Neutralizer (51 of 51) -# Modules from group: Energy Nosferatu (51 of 51) +# Modules from group: Energy Neutralizer (54 of 54) +# Modules from group: Energy Nosferatu (54 of 54) # Modules from group: Hull Repair Unit (25 of 25) # Modules from group: Remote Armor Repairer (39 of 39) # Modules from group: Remote Capacitor Transmitter (41 of 41) # Modules from group: Remote Shield Booster (38 of 38) # Modules from group: Smart Bomb (118 of 118) # Modules from group: Warp Disrupt Field Generator (7 of 7) -# Modules named like: Ancillary Remote (8 of 8) +# Modules named like: Remote Repairer (56 of 56) # Module: Reactive Armor Hardener # Module: Target Spectrum Breaker type = "overheat" diff --git a/eos/effects/overloadselfrangebonus.py b/eos/effects/overloadselfrangebonus.py index f306a064f..781bf4794 100644 --- a/eos/effects/overloadselfrangebonus.py +++ b/eos/effects/overloadselfrangebonus.py @@ -2,8 +2,8 @@ # # Used by: # Modules from group: Stasis Grappler (7 of 7) -# Modules from group: Stasis Web (18 of 18) -# Modules from group: Warp Scrambler (52 of 53) +# Modules from group: Stasis Web (19 of 19) +# Modules from group: Warp Scrambler (54 of 55) type = "overheat" diff --git a/eos/effects/overloadselfshieldbonusdurationbonus.py b/eos/effects/overloadselfshieldbonusdurationbonus.py index cc833be68..4706ccbca 100644 --- a/eos/effects/overloadselfshieldbonusdurationbonus.py +++ b/eos/effects/overloadselfshieldbonusdurationbonus.py @@ -1,8 +1,8 @@ # overloadSelfShieldBonusDurationBonus # # Used by: -# Modules from group: Ancillary Shield Booster (5 of 5) -# Modules from group: Shield Booster (93 of 93) +# Modules from group: Ancillary Shield Booster (8 of 8) +# Modules from group: Shield Booster (97 of 97) type = "overheat" diff --git a/eos/effects/overloadselfspeedbonus.py b/eos/effects/overloadselfspeedbonus.py index c7ee81a90..cc5dbe3f6 100644 --- a/eos/effects/overloadselfspeedbonus.py +++ b/eos/effects/overloadselfspeedbonus.py @@ -1,7 +1,7 @@ # overloadSelfSpeedBonus # # Used by: -# Modules from group: Propulsion Module (127 of 127) +# Modules from group: Propulsion Module (133 of 133) type = "overheat" diff --git a/eos/effects/pointdefense.py b/eos/effects/pointdefense.py index 4f2324035..44cf69af7 100644 --- a/eos/effects/pointdefense.py +++ b/eos/effects/pointdefense.py @@ -1,4 +1,7 @@ -# Not used by any item +# pointDefense +# +# Used by: +# Structure Modules from group: Structure Area Denial Module (2 of 2) type = 'active' diff --git a/eos/effects/poweroutputmultiply.py b/eos/effects/poweroutputmultiply.py index f4d5eb600..2add2137d 100644 --- a/eos/effects/poweroutputmultiply.py +++ b/eos/effects/poweroutputmultiply.py @@ -5,6 +5,7 @@ # Modules from group: Capacitor Power Relay (20 of 20) # Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Reactor Control Unit (22 of 22) +# Variations of structure module: Standup Reactor Control Unit I (2 of 2) type = "passive" diff --git a/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py b/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py deleted file mode 100644 index 2f2378405..000000000 --- a/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py +++ /dev/null @@ -1,10 +0,0 @@ -# probeLauncherCPUPercentBonusTacticalDestroyer -# -# Used by: -# Ships from group: Tactical Destroyer (4 of 4) -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Astrometrics"), - "cpu", ship.getModifiedItemAttr("roleBonusT3ProbeCPU")) diff --git a/eos/effects/projectileweapondamagemultiply.py b/eos/effects/projectileweapondamagemultiply.py index 711075efb..aeafffff7 100644 --- a/eos/effects/projectileweapondamagemultiply.py +++ b/eos/effects/projectileweapondamagemultiply.py @@ -1,7 +1,7 @@ # projectileWeaponDamageMultiply # # Used by: -# Modules from group: Gyrostabilizer (13 of 13) +# Modules from group: Gyrostabilizer (14 of 14) type = "passive" diff --git a/eos/effects/projectileweaponspeedmultiply.py b/eos/effects/projectileweaponspeedmultiply.py index 784712da1..73c1d538d 100644 --- a/eos/effects/projectileweaponspeedmultiply.py +++ b/eos/effects/projectileweaponspeedmultiply.py @@ -1,7 +1,7 @@ # projectileWeaponSpeedMultiply # # Used by: -# Modules from group: Gyrostabilizer (13 of 13) +# Modules from group: Gyrostabilizer (14 of 14) type = "passive" diff --git a/eos/effects/rechargerateaddpassive.py b/eos/effects/rechargerateaddpassive.py deleted file mode 100644 index 4f790674e..000000000 --- a/eos/effects/rechargerateaddpassive.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.increaseItemAttr("rechargeRate", module.getModifiedItemAttr("rechargeRate")) diff --git a/eos/effects/reconshipcloakcpubonus1.py b/eos/effects/reconshipcloakcpubonus1.py index 8f63a28f2..e7c05671c 100644 --- a/eos/effects/reconshipcloakcpubonus1.py +++ b/eos/effects/reconshipcloakcpubonus1.py @@ -1,7 +1,7 @@ # reconShipCloakCpuBonus1 # # Used by: -# Ships from group: Force Recon Ship (6 of 8) +# Ships from group: Force Recon Ship (7 of 9) type = "passive" runTime = "early" diff --git a/eos/effects/remotecapacitortransmitterpowerneedbonuseffect.py b/eos/effects/remotecapacitortransmitterpowerneedbonuseffect.py index f9c254dd9..54897ba12 100644 --- a/eos/effects/remotecapacitortransmitterpowerneedbonuseffect.py +++ b/eos/effects/remotecapacitortransmitterpowerneedbonuseffect.py @@ -1,7 +1,7 @@ # remoteCapacitorTransmitterPowerNeedBonusEffect # # Used by: -# Ships from group: Logistics (3 of 6) +# Ships from group: Logistics (3 of 7) type = "passive" diff --git a/eos/effects/remoteecmfalloff.py b/eos/effects/remoteecmfalloff.py index 409235895..3ab1e4010 100644 --- a/eos/effects/remoteecmfalloff.py +++ b/eos/effects/remoteecmfalloff.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: ECM (39 of 39) +# Starbases from group: Electronic Warfare Battery (12 of 12) from eos.modifiedAttributeDict import ModifiedAttributeDict type = "projected", "active" diff --git a/eos/effects/remotehullrepair.py b/eos/effects/remotehullrepair.py deleted file mode 100644 index 508b5684e..000000000 --- a/eos/effects/remotehullrepair.py +++ /dev/null @@ -1,11 +0,0 @@ -# Not used by any item -type = "projected", "active" -runTime = "late" - - -def handler(fit, module, context): - if "projected" not in context: - return - bonus = module.getModifiedItemAttr("structureDamageAmount") - duration = module.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotesensordampfalloff.py b/eos/effects/remotesensordampfalloff.py index 81ae5dfa1..cc60e1986 100644 --- a/eos/effects/remotesensordampfalloff.py +++ b/eos/effects/remotesensordampfalloff.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Sensor Dampener (6 of 6) +# Starbases from group: Sensor Dampening Battery (3 of 3) type = "projected", "active" diff --git a/eos/effects/remotewebifierfalloff.py b/eos/effects/remotewebifierfalloff.py index 4d5bc770c..c50e08141 100644 --- a/eos/effects/remotewebifierfalloff.py +++ b/eos/effects/remotewebifierfalloff.py @@ -2,7 +2,8 @@ # # Used by: # Modules from group: Stasis Grappler (7 of 7) -# Modules from group: Stasis Web (18 of 18) +# Modules from group: Stasis Web (19 of 19) +# Starbases from group: Stasis Webification Battery (3 of 3) type = "active", "projected" diff --git a/eos/effects/remotewebifiermaxrangebonus.py b/eos/effects/remotewebifiermaxrangebonus.py index 19498882a..531f18ded 100644 --- a/eos/effects/remotewebifiermaxrangebonus.py +++ b/eos/effects/remotewebifiermaxrangebonus.py @@ -2,9 +2,10 @@ # # Used by: # Implants named like: Inquest 'Eros' Stasis Webifier MR (3 of 3) +# Implants named like: Inquest 'Hedone' Entanglement Optimizer WS (3 of 3) type = "passive" def handler(fit, src, context): fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Stasis Web", "maxRange", - src.getModifiedItemAttr("stasisWebRangeBonus"), stackingPenalties=True) + src.getModifiedItemAttr("stasisWebRangeBonus"), stackingPenalties=False) diff --git a/eos/effects/rigdrawbackbonuseffect.py b/eos/effects/rigdrawbackbonuseffect.py deleted file mode 100644 index 58f066b1d..000000000 --- a/eos/effects/rigdrawbackbonuseffect.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, skill, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), - "drawback", skill.getModifiedItemAttr("rigDrawbackBonus") * skill.level) diff --git a/eos/effects/rolebonuswarpspeed.py b/eos/effects/rolebonuswarpspeed.py new file mode 100644 index 000000000..2532ab94a --- /dev/null +++ b/eos/effects/rolebonuswarpspeed.py @@ -0,0 +1,13 @@ +# roleBonusWarpSpeed +# +# Used by: +# Ship: Cynabal +# Ship: Dramiel +# Ship: Leopard +# Ship: Machariel +# Ship: Victorieux Luxury Yacht +type = "passive" + + +def handler(fit, src, context): + fit.ship.boostItemAttr("warpSpeedMultiplier", src.getModifiedItemAttr("shipRoleBonusWarpSpeed")) diff --git a/eos/effects/rolebonuswdrange.py b/eos/effects/rolebonuswdrange.py index 3d6cbdf24..02e61cf78 100644 --- a/eos/effects/rolebonuswdrange.py +++ b/eos/effects/rolebonuswdrange.py @@ -6,7 +6,7 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Weapon Disruption"), "falloff", + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Weapon Disruption"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonus")) fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Weapon Disruption"), "maxRange", src.getModifiedItemAttr("roleBonus")) diff --git a/eos/effects/scanresolutionaddpassive.py b/eos/effects/scanresolutionaddpassive.py deleted file mode 100644 index 6e4599d7b..000000000 --- a/eos/effects/scanresolutionaddpassive.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.increaseItemAttr("scanResolution", module.getModifiedItemAttr("scanResolution")) diff --git a/eos/effects/scanstrengthaddpassive.py b/eos/effects/scanstrengthaddpassive.py deleted file mode 100644 index 12f228a0d..000000000 --- a/eos/effects/scanstrengthaddpassive.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - sensorTypes = ("Gravimetric", "Ladar", "Magnetometric", "Radar") - for sensorType in sensorTypes: - sensAttr = "scan{0}Strength".format(sensorType) - fit.ship.increaseItemAttr(sensAttr, module.getModifiedItemAttr(sensAttr)) diff --git a/eos/effects/scanstrengthbonuspercentactivate.py b/eos/effects/scanstrengthbonuspercentactivate.py deleted file mode 100644 index 10b66e3c4..000000000 --- a/eos/effects/scanstrengthbonuspercentactivate.py +++ /dev/null @@ -1,11 +0,0 @@ -# Not used by any item -type = "active" - - -def handler(fit, module, context): - for scanType in ("Gravimetric", "Magnetometric", "Radar", "Ladar"): - fit.ship.boostItemAttr( - "scan{}Strength".format(scanType), - module.getModifiedItemAttr("scan{}StrengthPercent".format(scanType)), - stackingPenalties=True - ) diff --git a/eos/effects/servicemodulefullpowerhitpointpostassign.py b/eos/effects/servicemodulefullpowerhitpointpostassign.py index edcb40ae1..a4f574110 100644 --- a/eos/effects/servicemodulefullpowerhitpointpostassign.py +++ b/eos/effects/servicemodulefullpowerhitpointpostassign.py @@ -1,4 +1,11 @@ -# Not used by any item +# serviceModuleFullPowerHitpointPostAssign +# +# Used by: +# Structure Modules from group: Structure Citadel Service Module (2 of 2) +# Structure Modules from group: Structure Engineering Service Module (6 of 6) +# Structure Modules from group: Structure Navigation Service Module (3 of 3) +# Structure Modules from group: Structure Resource Processing Service Module (4 of 4) +# Structure Module: Standup Moon Drill I type = "passive" runTime = "early" diff --git a/eos/effects/shieldboostamplifierpassive.py b/eos/effects/shieldboostamplifierpassive.py index c1361e338..182c04acf 100644 --- a/eos/effects/shieldboostamplifierpassive.py +++ b/eos/effects/shieldboostamplifierpassive.py @@ -1,7 +1,6 @@ # shieldBoostAmplifierPassive # # Used by: -# Implants named like: Agency 'Hardshell' TB Dose (3 of 3) # Implants named like: grade Crystal (15 of 18) type = "passive" diff --git a/eos/effects/shieldboostamplifierpassivebooster.py b/eos/effects/shieldboostamplifierpassivebooster.py index 0c7158194..30432474d 100644 --- a/eos/effects/shieldboostamplifierpassivebooster.py +++ b/eos/effects/shieldboostamplifierpassivebooster.py @@ -1,6 +1,7 @@ # shieldBoostAmplifierPassiveBooster # # Used by: +# Implants named like: Agency 'Hardshell' TB Dose (4 of 4) # Implants named like: Blue Pill Booster (5 of 5) # Implant: Antipharmakon Thureo type = "passive" diff --git a/eos/effects/shieldboosting.py b/eos/effects/shieldboosting.py index 8d8676d0e..808e2f1b0 100644 --- a/eos/effects/shieldboosting.py +++ b/eos/effects/shieldboosting.py @@ -1,7 +1,7 @@ # shieldBoosting # # Used by: -# Modules from group: Shield Booster (93 of 93) +# Modules from group: Shield Booster (97 of 97) runTime = "late" type = "active" diff --git a/eos/effects/shieldcapacitybonusonline.py b/eos/effects/shieldcapacitybonusonline.py index 7c4cb037d..cba8f70bb 100644 --- a/eos/effects/shieldcapacitybonusonline.py +++ b/eos/effects/shieldcapacitybonusonline.py @@ -1,7 +1,7 @@ # shieldCapacityBonusOnline # # Used by: -# Modules from group: Shield Extender (33 of 33) +# Modules from group: Shield Extender (36 of 36) # Modules from group: Shield Resistance Amplifier (88 of 88) type = "passive" diff --git a/eos/effects/shieldrechargerateaddpassive.py b/eos/effects/shieldrechargerateaddpassive.py deleted file mode 100644 index 1d071fb8a..000000000 --- a/eos/effects/shieldrechargerateaddpassive.py +++ /dev/null @@ -1,6 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.increaseItemAttr("shieldRechargeRate", module.getModifiedItemAttr("shieldRechargeRate") or 0) diff --git a/eos/effects/shieldtransfer.py b/eos/effects/shieldtransfer.py deleted file mode 100644 index 54bbc1112..000000000 --- a/eos/effects/shieldtransfer.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "projected", "active" - - -def handler(fit, container, context): - if "projected" in context: - bonus = container.getModifiedItemAttr("shieldBonus") - duration = container.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("shieldRepair", bonus / duration) diff --git a/eos/effects/shieldtransportcpuneedbonuseffect.py b/eos/effects/shieldtransportcpuneedbonuseffect.py index bd96b3efc..427290a3f 100644 --- a/eos/effects/shieldtransportcpuneedbonuseffect.py +++ b/eos/effects/shieldtransportcpuneedbonuseffect.py @@ -1,7 +1,7 @@ # shieldTransportCpuNeedBonusEffect # # Used by: -# Ships from group: Logistics (3 of 6) +# Ships from group: Logistics (3 of 7) type = "passive" diff --git a/eos/effects/shiparmoremresistancepbc2.py b/eos/effects/shiparmoremresistancepbc2.py new file mode 100644 index 000000000..8fcf35f0e --- /dev/null +++ b/eos/effects/shiparmoremresistancepbc2.py @@ -0,0 +1,10 @@ +# shipArmorEMResistancePBC2 +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.ship.boostItemAttr("armorEmDamageResonance", ship.getModifiedItemAttr("shipBonusPBC2"), + skill="Precursor Battlecruiser") diff --git a/eos/effects/shiparmorexplosiveresistancepbc2.py b/eos/effects/shiparmorexplosiveresistancepbc2.py new file mode 100644 index 000000000..588feda67 --- /dev/null +++ b/eos/effects/shiparmorexplosiveresistancepbc2.py @@ -0,0 +1,10 @@ +# shipArmorExplosiveResistancePBC2 +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.ship.boostItemAttr("armorExplosiveDamageResonance", ship.getModifiedItemAttr("shipBonusPBC2"), + skill="Precursor Battlecruiser") diff --git a/eos/effects/shiparmorkineticresistancepbc2.py b/eos/effects/shiparmorkineticresistancepbc2.py new file mode 100644 index 000000000..0248e9374 --- /dev/null +++ b/eos/effects/shiparmorkineticresistancepbc2.py @@ -0,0 +1,10 @@ +# shipArmorKineticResistancePBC2 +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.ship.boostItemAttr("armorKineticDamageResonance", ship.getModifiedItemAttr("shipBonusPBC2"), + skill="Precursor Battlecruiser") diff --git a/eos/effects/shiparmorthermalresistancepbc2.py b/eos/effects/shiparmorthermalresistancepbc2.py new file mode 100644 index 000000000..99149f084 --- /dev/null +++ b/eos/effects/shiparmorthermalresistancepbc2.py @@ -0,0 +1,10 @@ +# shipArmorThermalResistancePBC2 +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.ship.boostItemAttr("armorThermalDamageResonance", ship.getModifiedItemAttr("shipBonusPBC2"), + skill="Precursor Battlecruiser") diff --git a/eos/effects/shipbonusdreadnoughtc2shieldresists.py b/eos/effects/shipbonusdreadnoughtc2shieldresists.py index f89b07a23..6a533902c 100644 --- a/eos/effects/shipbonusdreadnoughtc2shieldresists.py +++ b/eos/effects/shipbonusdreadnoughtc2shieldresists.py @@ -1,8 +1,7 @@ # shipBonusDreadnoughtC2ShieldResists # # Used by: -# Ship: Caiman -# Ship: Phoenix +# Variations of ship: Phoenix (2 of 2) type = "passive" diff --git a/eos/effects/shipbonusdroneminingamountac2.py b/eos/effects/shipbonusdroneminingamountac2.py index 4e314a7b7..0fc338bf7 100644 --- a/eos/effects/shipbonusdroneminingamountac2.py +++ b/eos/effects/shipbonusdroneminingamountac2.py @@ -6,5 +6,5 @@ type = "passive" def handler(fit, ship, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), "miningAmount", ship.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") diff --git a/eos/effects/shipbonusdroneminingamountgc2.py b/eos/effects/shipbonusdroneminingamountgc2.py index b13344744..e1bbc1314 100644 --- a/eos/effects/shipbonusdroneminingamountgc2.py +++ b/eos/effects/shipbonusdroneminingamountgc2.py @@ -7,5 +7,5 @@ type = "passive" def handler(fit, ship, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Mining Drone Operation"), "miningAmount", ship.getModifiedItemAttr("shipBonusGC2"), skill="Gallente Cruiser") diff --git a/eos/effects/shipbonusenergyvampirerangead2.py b/eos/effects/shipbonusenergyvampirerangead2.py deleted file mode 100644 index 28f9f7c8c..000000000 --- a/eos/effects/shipbonusenergyvampirerangead2.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", - "maxRange", ship.getModifiedItemAttr("shipBonusAD2"), skill="Amarr Destroyer") diff --git a/eos/effects/shipbonusforceauxiliarya3capcapacity.py b/eos/effects/shipbonusforceauxiliarya3capcapacity.py deleted file mode 100644 index 604573a39..000000000 --- a/eos/effects/shipbonusforceauxiliarya3capcapacity.py +++ /dev/null @@ -1,10 +0,0 @@ -# shipBonusForceAuxiliaryA3CapCapacity -# -# Used by: -# Ship: Apostle -type = "passive" - - -def handler(fit, src, context): - fit.ship.boostItemAttr("capacitorCapacity", src.getModifiedItemAttr("shipBonusForceAuxiliaryA3"), - skill="Amarr Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryc2shieldresists.py b/eos/effects/shipbonusforceauxiliaryc2shieldresists.py index bcdee6cdf..b18ae263a 100644 --- a/eos/effects/shipbonusforceauxiliaryc2shieldresists.py +++ b/eos/effects/shipbonusforceauxiliaryc2shieldresists.py @@ -1,8 +1,7 @@ # shipBonusForceAuxiliaryC2ShieldResists # # Used by: -# Ship: Loggerhead -# Ship: Minokawa +# Variations of ship: Minokawa (2 of 2) type = "passive" diff --git a/eos/effects/shipbonusforceauxiliaryc3capcapacity.py b/eos/effects/shipbonusforceauxiliaryc3capcapacity.py deleted file mode 100644 index e7ed1f5de..000000000 --- a/eos/effects/shipbonusforceauxiliaryc3capcapacity.py +++ /dev/null @@ -1,10 +0,0 @@ -# shipBonusForceAuxiliaryC3CapCapacity -# -# Used by: -# Ship: Minokawa -type = "passive" - - -def handler(fit, src, context): - fit.ship.boostItemAttr("capacitorCapacity", src.getModifiedItemAttr("shipBonusForceAuxiliaryC3"), - skill="Caldari Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryg3capboosterstrength.py b/eos/effects/shipbonusforceauxiliaryg3capboosterstrength.py deleted file mode 100644 index 7dbd8b392..000000000 --- a/eos/effects/shipbonusforceauxiliaryg3capboosterstrength.py +++ /dev/null @@ -1,10 +0,0 @@ -# shipBonusForceAuxiliaryG3CapBoosterStrength -# -# Used by: -# Ship: Ninazu -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Capacitor Booster Charge", "capacitorBonus", - src.getModifiedItemAttr("shipBonusForceAuxiliaryG3"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonusforceauxiliarym3capboosterstrength.py b/eos/effects/shipbonusforceauxiliarym3capboosterstrength.py deleted file mode 100644 index a2a656cea..000000000 --- a/eos/effects/shipbonusforceauxiliarym3capboosterstrength.py +++ /dev/null @@ -1,10 +0,0 @@ -# shipBonusForceAuxiliaryM3CapBoosterStrength -# -# Used by: -# Ship: Lif -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Capacitor Booster Charge", "capacitorBonus", - src.getModifiedItemAttr("shipBonusForceAuxiliaryM3"), skill="Minmatar Carrier") diff --git a/eos/effects/shipbonusminingdroneamountpercentrookie.py b/eos/effects/shipbonusminingdroneamountpercentrookie.py index 5cde2c087..5e0be29e2 100644 --- a/eos/effects/shipbonusminingdroneamountpercentrookie.py +++ b/eos/effects/shipbonusminingdroneamountpercentrookie.py @@ -9,5 +9,5 @@ type = "passive" def handler(fit, container, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Mining Drone Operation"), "miningAmount", container.getModifiedItemAttr("rookieDroneBonus")) diff --git a/eos/effects/shipbonusmutadaptiveremoterepairrangerole3.py b/eos/effects/shipbonusmutadaptiveremoterepairrangerole3.py new file mode 100644 index 000000000..64fca9ffb --- /dev/null +++ b/eos/effects/shipbonusmutadaptiveremoterepairrangerole3.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRemoteRepairRangeRole3 +# +# Used by: +# Ship: Rodiva +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "maxRange", src.getModifiedItemAttr("shipBonusRole3")) diff --git a/eos/effects/shipbonusmutadaptiveremoterepamountelitebonuslogisitics2.py b/eos/effects/shipbonusmutadaptiveremoterepamountelitebonuslogisitics2.py new file mode 100644 index 000000000..b8ae258c3 --- /dev/null +++ b/eos/effects/shipbonusmutadaptiveremoterepamountelitebonuslogisitics2.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRemoteRepAmounteliteBonusLogisitics2 +# +# Used by: +# Ship: Zarmazd +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "armorDamageAmount", src.getModifiedItemAttr("eliteBonusLogistics2"), skill="Logistics Cruisers") diff --git a/eos/effects/shipbonusmutadaptiveremoterepcapneedelitebonuslogisitics1.py b/eos/effects/shipbonusmutadaptiveremoterepcapneedelitebonuslogisitics1.py new file mode 100644 index 000000000..b936f644c --- /dev/null +++ b/eos/effects/shipbonusmutadaptiveremoterepcapneedelitebonuslogisitics1.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRemoteRepCapNeedeliteBonusLogisitics1 +# +# Used by: +# Ship: Zarmazd +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "capacitorNeed", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers") diff --git a/eos/effects/shipbonusmutadaptiveremotereprangepc1.py b/eos/effects/shipbonusmutadaptiveremotereprangepc1.py new file mode 100644 index 000000000..67b2da620 --- /dev/null +++ b/eos/effects/shipbonusmutadaptiveremotereprangepc1.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRemoteRepRangePC1 +# +# Used by: +# Ship: Zarmazd +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "maxRange", src.getModifiedItemAttr("shipBonusPC1"), skill="Precursor Cruiser") diff --git a/eos/effects/shipbonusmutadaptiverepamountpc1.py b/eos/effects/shipbonusmutadaptiverepamountpc1.py new file mode 100644 index 000000000..7e892f3e4 --- /dev/null +++ b/eos/effects/shipbonusmutadaptiverepamountpc1.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRepAmountPC1 +# +# Used by: +# Ship: Rodiva +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "armorDamageAmount", src.getModifiedItemAttr("shipBonusPC1"), skill="Precursor Cruiser") diff --git a/eos/effects/shipbonusmutadaptiverepcapneedpc2.py b/eos/effects/shipbonusmutadaptiverepcapneedpc2.py new file mode 100644 index 000000000..22348514a --- /dev/null +++ b/eos/effects/shipbonusmutadaptiverepcapneedpc2.py @@ -0,0 +1,7 @@ +# shipBonusMutadaptiveRepCapNeedPC2 +# +# Used by: +# Ship: Rodiva +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Mutadaptive Remote Armor Repairer", "capacitorNeed", src.getModifiedItemAttr("shipBonusPC2"), skill="Precursor Cruiser") diff --git a/eos/effects/shipbonusneutcapneedrolebonus2.py b/eos/effects/shipbonusneutcapneedrolebonus2.py new file mode 100644 index 000000000..52dfa7796 --- /dev/null +++ b/eos/effects/shipbonusneutcapneedrolebonus2.py @@ -0,0 +1,16 @@ +# shipBonusNeutCapNeedRoleBonus2 +# +# Used by: +# Ship: Damavik +# Ship: Drekavac +# Ship: Hydra +# Ship: Kikimora +# Ship: Leshak +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Neutralizer", "capacitorNeed", + src.getModifiedItemAttr("shipBonusRole2")) diff --git a/eos/effects/shipbonusnosneutcapneedrolebonus2.py b/eos/effects/shipbonusnosneutcapneedrolebonus2.py new file mode 100644 index 000000000..ec427ec11 --- /dev/null +++ b/eos/effects/shipbonusnosneutcapneedrolebonus2.py @@ -0,0 +1,7 @@ +# shipBonusNosNeutCapNeedRoleBonus2 +# +# Used by: +# Variations of ship: Rodiva (2 of 2) +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capacitor Emission Systems"), "capacitorNeed", src.getModifiedItemAttr("shipBonusRole2")) diff --git a/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py b/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py deleted file mode 100644 index e8860ee63..000000000 --- a/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - for type in ("shieldCapacity", "armorHP", "hp"): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - type, ship.getModifiedItemAttr("shipBonusORECapital4"), - skill="Capital Industrial Ships") diff --git a/eos/effects/shipbonusorecapshipdronedmgbonus.py b/eos/effects/shipbonusorecapshipdronedmgbonus.py deleted file mode 100644 index eaa95fe6d..000000000 --- a/eos/effects/shipbonusorecapshipdronedmgbonus.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "damageMultiplier", ship.getModifiedItemAttr("shipBonusORECapital4"), - skill="Capital Industrial Ships") diff --git a/eos/effects/shipbonuspbc1disintegratordamage.py b/eos/effects/shipbonuspbc1disintegratordamage.py new file mode 100644 index 000000000..da8e257ea --- /dev/null +++ b/eos/effects/shipbonuspbc1disintegratordamage.py @@ -0,0 +1,11 @@ +# shipBonusPBC1DisintegratorDamage +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPBC1"), + skill="Precursor Battlecruiser") diff --git a/eos/effects/shipbonuspctdamagepc1.py b/eos/effects/shipbonuspctdamagepc1.py new file mode 100644 index 000000000..06e686ab2 --- /dev/null +++ b/eos/effects/shipbonuspctdamagepc1.py @@ -0,0 +1,11 @@ +# shipbonusPCTDamagePC1 +# +# Used by: +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPC1"), skill="Precursor Cruiser") diff --git a/eos/effects/shipbonuspctdamagepf1.py b/eos/effects/shipbonuspctdamagepf1.py new file mode 100644 index 000000000..59a1071e2 --- /dev/null +++ b/eos/effects/shipbonuspctdamagepf1.py @@ -0,0 +1,11 @@ +# shipbonusPCTDamagePF1 +# +# Used by: +# Ship: Damavik +# Ship: Hydra +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPF1"), skill="Precursor Frigate") diff --git a/eos/effects/shipbonuspctoptimalpf2.py b/eos/effects/shipbonuspctoptimalpf2.py new file mode 100644 index 000000000..7403b2146 --- /dev/null +++ b/eos/effects/shipbonuspctoptimalpf2.py @@ -0,0 +1,11 @@ +# shipbonusPCTOptimalPF2 +# +# Used by: +# Ship: Damavik +# Ship: Hydra +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "maxRange", ship.getModifiedItemAttr("shipBonusPF2"), skill="Precursor Frigate") diff --git a/eos/effects/shipbonuspcttrackingpc2.py b/eos/effects/shipbonuspcttrackingpc2.py new file mode 100644 index 000000000..4914d290e --- /dev/null +++ b/eos/effects/shipbonuspcttrackingpc2.py @@ -0,0 +1,11 @@ +# shipbonusPCTTrackingPC2 +# +# Used by: +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), + "trackingSpeed", ship.getModifiedItemAttr("shipBonusPC2"), skill="Precursor Cruiser") diff --git a/eos/effects/shipbonuspd1disintegratordamage.py b/eos/effects/shipbonuspd1disintegratordamage.py new file mode 100644 index 000000000..23adcba2d --- /dev/null +++ b/eos/effects/shipbonuspd1disintegratordamage.py @@ -0,0 +1,11 @@ +# shipBonusPD1DisintegratorDamage +# +# Used by: +# Ship: Kikimora +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPD1"), + skill="Precursor Destroyer") diff --git a/eos/effects/shipbonuspd2disintegratormaxrange.py b/eos/effects/shipbonuspd2disintegratormaxrange.py new file mode 100644 index 000000000..c56940a22 --- /dev/null +++ b/eos/effects/shipbonuspd2disintegratormaxrange.py @@ -0,0 +1,11 @@ +# shipBonusPD2DisintegratorMaxRange +# +# Used by: +# Ship: Kikimora +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "maxRange", ship.getModifiedItemAttr("shipBonusPD2"), + skill="Precursor Destroyer") diff --git a/eos/effects/shipbonusremotecapacitortransferrangerole1.py b/eos/effects/shipbonusremotecapacitortransferrangerole1.py new file mode 100644 index 000000000..bfd670862 --- /dev/null +++ b/eos/effects/shipbonusremotecapacitortransferrangerole1.py @@ -0,0 +1,7 @@ +# shipBonusRemoteCapacitorTransferRangeRole1 +# +# Used by: +# Variations of ship: Rodiva (2 of 2) +type = "passive" +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Remote Capacitor Transmitter", "maxRange", src.getModifiedItemAttr("shipBonusRole1")) diff --git a/eos/effects/shipbonusremoterepcapneedrolebonus2.py b/eos/effects/shipbonusremoterepcapneedrolebonus2.py new file mode 100644 index 000000000..ac4ef0aa0 --- /dev/null +++ b/eos/effects/shipbonusremoterepcapneedrolebonus2.py @@ -0,0 +1,16 @@ +# shipBonusRemoteRepCapNeedRoleBonus2 +# +# Used by: +# Ship: Damavik +# Ship: Drekavac +# Ship: Hydra +# Ship: Kikimora +# Ship: Leshak +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "capacitorNeed", ship.getModifiedItemAttr("shipBonusRole2")) diff --git a/eos/effects/shipbonusremoterepmaxrangerolebonus1.py b/eos/effects/shipbonusremoterepmaxrangerolebonus1.py new file mode 100644 index 000000000..31963a4da --- /dev/null +++ b/eos/effects/shipbonusremoterepmaxrangerolebonus1.py @@ -0,0 +1,16 @@ +# shipBonusRemoteRepMaxRangeRoleBonus1 +# +# Used by: +# Ship: Damavik +# Ship: Drekavac +# Ship: Hydra +# Ship: Kikimora +# Ship: Leshak +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "maxRange", ship.getModifiedItemAttr("shipBonusRole1")) diff --git a/eos/effects/shipbonusrole3xltorpdeovelocitybonus.py b/eos/effects/shipbonusrole3xltorpdeovelocitybonus.py index 0c383ef58..02023dc88 100644 --- a/eos/effects/shipbonusrole3xltorpdeovelocitybonus.py +++ b/eos/effects/shipbonusrole3xltorpdeovelocitybonus.py @@ -1,8 +1,7 @@ # shipBonusRole3XLTorpdeoVelocityBonus # # Used by: -# Ship: Komodo -# Ship: Leviathan +# Variations of ship: Leviathan (2 of 2) type = "passive" diff --git a/eos/effects/shipbonusrole5remotearmorrepairpowergridbonus.py b/eos/effects/shipbonusrole5remotearmorrepairpowergridbonus.py index 3f764d908..5e87c2241 100644 --- a/eos/effects/shipbonusrole5remotearmorrepairpowergridbonus.py +++ b/eos/effects/shipbonusrole5remotearmorrepairpowergridbonus.py @@ -1,7 +1,7 @@ # shipBonusRole5RemoteArmorRepairPowergridBonus # # Used by: -# Ships from group: Logistics (3 of 6) +# Ships from group: Logistics (3 of 7) type = "passive" diff --git a/eos/effects/shipbonussmartbombcapneedrolebonus2.py b/eos/effects/shipbonussmartbombcapneedrolebonus2.py new file mode 100644 index 000000000..69e4017a8 --- /dev/null +++ b/eos/effects/shipbonussmartbombcapneedrolebonus2.py @@ -0,0 +1,17 @@ +# shipBonusSmartbombCapNeedRoleBonus2 +# +# Used by: +# Variations of ship: Rodiva (2 of 2) +# Ship: Damavik +# Ship: Drekavac +# Ship: Hydra +# Ship: Kikimora +# Ship: Leshak +# Ship: Tiamat +# Ship: Vedmak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Energy Pulse Weapons"), + "capacitorNeed", ship.getModifiedItemAttr("shipBonusRole2")) diff --git a/eos/effects/shipbonussurveyprobeexplosiondelayskillsurveycovertops3.py b/eos/effects/shipbonussurveyprobeexplosiondelayskillsurveycovertops3.py index db215459d..4fc7124c5 100644 --- a/eos/effects/shipbonussurveyprobeexplosiondelayskillsurveycovertops3.py +++ b/eos/effects/shipbonussurveyprobeexplosiondelayskillsurveycovertops3.py @@ -1,7 +1,7 @@ # shipBonusSurveyProbeExplosionDelaySkillSurveyCovertOps3 # # Used by: -# Ships from group: Covert Ops (5 of 7) +# Ships from group: Covert Ops (5 of 8) type = "passive" diff --git a/eos/effects/shipbonustitana3warpstrength.py b/eos/effects/shipbonustitana3warpstrength.py index e5fee5ffc..f89d421c4 100644 --- a/eos/effects/shipbonustitana3warpstrength.py +++ b/eos/effects/shipbonustitana3warpstrength.py @@ -1,8 +1,7 @@ # shipBonusTitanA3WarpStrength # # Used by: -# Ship: Avatar -# Ship: Molok +# Variations of ship: Avatar (2 of 2) type = "passive" diff --git a/eos/effects/shipbonustitanc2rofbonus.py b/eos/effects/shipbonustitanc2rofbonus.py index 78d592e85..52ce60bda 100644 --- a/eos/effects/shipbonustitanc2rofbonus.py +++ b/eos/effects/shipbonustitanc2rofbonus.py @@ -1,8 +1,7 @@ # shipBonusTitanC2ROFBonus # # Used by: -# Ship: Komodo -# Ship: Leviathan +# Variations of ship: Leviathan (2 of 2) type = "passive" diff --git a/eos/effects/shipbonustitanc3warpstrength.py b/eos/effects/shipbonustitanc3warpstrength.py index 67186a13b..2932f5fc1 100644 --- a/eos/effects/shipbonustitanc3warpstrength.py +++ b/eos/effects/shipbonustitanc3warpstrength.py @@ -1,8 +1,7 @@ # shipBonusTitanC3WarpStrength # # Used by: -# Ship: Komodo -# Ship: Leviathan +# Variations of ship: Leviathan (2 of 2) type = "passive" diff --git a/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py b/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py deleted file mode 100644 index 76673f21f..000000000 --- a/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - if fit.extraAttributes["siege"]: - fit.ship.increaseItemAttr("commandBonusEffective", ship.getModifiedItemAttr("shipBonusORECapital2"), - skill="Capital Industrial Ships") diff --git a/eos/effects/shipdronescoutthermaldamagegf2.py b/eos/effects/shipdronescoutthermaldamagegf2.py index afe18cb9d..54f7716e0 100644 --- a/eos/effects/shipdronescoutthermaldamagegf2.py +++ b/eos/effects/shipdronescoutthermaldamagegf2.py @@ -6,5 +6,5 @@ type = "passive" def handler(fit, ship, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drone Avionics"), + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Light Drone Operation"), "thermalDamage", ship.getModifiedItemAttr("shipBonusGF2"), skill="Gallente Frigate") diff --git a/eos/effects/shipmaxlockedtargetsbonusaddonline.py b/eos/effects/shipmaxlockedtargetsbonusaddonline.py index 5e33906ff..f5ab7cb30 100644 --- a/eos/effects/shipmaxlockedtargetsbonusaddonline.py +++ b/eos/effects/shipmaxlockedtargetsbonusaddonline.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Signal Amplifier (7 of 7) +# Structure Modules from group: Structure Signal Amplifier (2 of 2) type = "passive" diff --git a/eos/effects/shipmaxtargetrangebonusonline.py b/eos/effects/shipmaxtargetrangebonusonline.py index 520230668..fb6a1520a 100644 --- a/eos/effects/shipmaxtargetrangebonusonline.py +++ b/eos/effects/shipmaxtargetrangebonusonline.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Signal Amplifier (7 of 7) +# Structure Modules from group: Structure Signal Amplifier (2 of 2) type = "passive" diff --git a/eos/effects/shipmodescanrespostdiv.py b/eos/effects/shipmodescanrespostdiv.py deleted file mode 100644 index 2ad04ec22..000000000 --- a/eos/effects/shipmodescanrespostdiv.py +++ /dev/null @@ -1,11 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.multiplyItemAttr( - "scanResolution", - 1 / module.getModifiedItemAttr("modeScanResPostDiv"), - stackingPenalties=True, - penaltyGroup="postDiv" - ) diff --git a/eos/effects/shipmoduleancillaryremotearmorrepairer.py b/eos/effects/shipmoduleancillaryremotearmorrepairer.py index 4085aaeeb..a1697eec7 100644 --- a/eos/effects/shipmoduleancillaryremotearmorrepairer.py +++ b/eos/effects/shipmoduleancillaryremotearmorrepairer.py @@ -2,8 +2,9 @@ # # Used by: # Modules from group: Ancillary Remote Armor Repairer (4 of 4) -runTime = "late" + type = "projected", "active" +runTime = "late" def handler(fit, module, context, **kwargs): @@ -17,4 +18,7 @@ def handler(fit, module, context, **kwargs): amount = module.getModifiedItemAttr("armorDamageAmount") * multiplier speed = module.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", amount / speed, **kwargs) + rps = amount / speed + fit.extraAttributes.increase("armorRepair", rps) + fit.extraAttributes.increase("armorRepairPreSpool", rps) + fit.extraAttributes.increase("armorRepairFullSpool", rps) diff --git a/eos/effects/shipmoduleancillaryremoteshieldbooster.py b/eos/effects/shipmoduleancillaryremoteshieldbooster.py index 5fbcb0ee0..dc135bc0a 100644 --- a/eos/effects/shipmoduleancillaryremoteshieldbooster.py +++ b/eos/effects/shipmoduleancillaryremoteshieldbooster.py @@ -2,8 +2,9 @@ # # Used by: # Modules from group: Ancillary Remote Shield Booster (4 of 4) -runTime = "late" + type = "projected", "active" +runTime = "late" def handler(fit, module, context, **kwargs): diff --git a/eos/effects/shipmoduleremotearmormutadaptiverepairer.py b/eos/effects/shipmoduleremotearmormutadaptiverepairer.py new file mode 100644 index 000000000..ec98c1e23 --- /dev/null +++ b/eos/effects/shipmoduleremotearmormutadaptiverepairer.py @@ -0,0 +1,28 @@ +# ShipModuleRemoteArmorMutadaptiveRepairer +# +# Used by: +# Modules from group: Mutadaptive Remote Armor Repairer (5 of 5) + + +from eos.utils.spoolSupport import SpoolType, SpoolOptions, calculateSpoolup, resolveSpoolOptions + + +type = "projected", "active" +runTime = "late" + + +def handler(fit, container, context, **kwargs): + if "projected" in context: + repAmountBase = container.getModifiedItemAttr("armorDamageAmount") + cycleTime = container.getModifiedItemAttr("duration") / 1000.0 + repSpoolMax = container.getModifiedItemAttr("repairMultiplierBonusMax") + repSpoolPerCycle = container.getModifiedItemAttr("repairMultiplierBonusPerCycle") + # TODO: fetch spoolup option + defaultSpoolValue = 1 + spoolType, spoolAmount = resolveSpoolOptions(SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False), container) + rps = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0]) / cycleTime + rpsPreSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SCALE, 0)[0]) / cycleTime + rpsFullSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SCALE, 1)[0]) / cycleTime + fit.extraAttributes.increase("armorRepair", rps, **kwargs) + fit.extraAttributes.increase("armorRepairPreSpool", rpsPreSpool, **kwargs) + fit.extraAttributes.increase("armorRepairFullSpool", rpsFullSpool, **kwargs) diff --git a/eos/effects/shipmoduleremotearmorrepairer.py b/eos/effects/shipmoduleremotearmorrepairer.py index 706dd74ee..220792812 100644 --- a/eos/effects/shipmoduleremotearmorrepairer.py +++ b/eos/effects/shipmoduleremotearmorrepairer.py @@ -2,11 +2,16 @@ # # Used by: # Modules from group: Remote Armor Repairer (39 of 39) + type = "projected", "active" +runTime = "late" def handler(fit, container, context, **kwargs): if "projected" in context: bonus = container.getModifiedItemAttr("armorDamageAmount") duration = container.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", bonus / duration, **kwargs) + rps = bonus / duration + fit.extraAttributes.increase("armorRepair", rps) + fit.extraAttributes.increase("armorRepairPreSpool", rps) + fit.extraAttributes.increase("armorRepairFullSpool", rps) diff --git a/eos/effects/shipmoduleremotecapacitortransmitter.py b/eos/effects/shipmoduleremotecapacitortransmitter.py index 9ea44beb6..6b414da7e 100644 --- a/eos/effects/shipmoduleremotecapacitortransmitter.py +++ b/eos/effects/shipmoduleremotecapacitortransmitter.py @@ -2,8 +2,13 @@ # # Used by: # Modules from group: Remote Capacitor Transmitter (41 of 41) + + from eos.modifiedAttributeDict import ModifiedAttributeDict + + type = "projected", "active" +runTime = "late" def handler(fit, src, context, **kwargs): diff --git a/eos/effects/shipmoduleremotehullrepairer.py b/eos/effects/shipmoduleremotehullrepairer.py index 5b4cce596..94839fdff 100644 --- a/eos/effects/shipmoduleremotehullrepairer.py +++ b/eos/effects/shipmoduleremotehullrepairer.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Remote Hull Repairer (8 of 8) + type = "projected", "active" runTime = "late" diff --git a/eos/effects/shippcbsdmgbonuspcbs2.py b/eos/effects/shippcbsdmgbonuspcbs2.py new file mode 100644 index 000000000..3f35b237c --- /dev/null +++ b/eos/effects/shippcbsdmgbonuspcbs2.py @@ -0,0 +1,10 @@ +# shipPCBSDmgBonusPCBS2 +# +# Used by: +# Ship: Leshak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Precursor Weapon"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPBS2"), skill="Precursor Battleship") diff --git a/eos/effects/shippcbsspeedbonuspcbs1.py b/eos/effects/shippcbsspeedbonuspcbs1.py new file mode 100644 index 000000000..9a35e373c --- /dev/null +++ b/eos/effects/shippcbsspeedbonuspcbs1.py @@ -0,0 +1,10 @@ +# shipPCBSSPeedBonusPCBS1 +# +# Used by: +# Ship: Leshak +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Precursor Weapon"), + "speed", ship.getModifiedItemAttr("shipBonusPBS1"), skill="Precursor Battleship") diff --git a/eos/effects/shippdmgbonusmf.py b/eos/effects/shippdmgbonusmf.py index 0a359917e..c7ad6a186 100644 --- a/eos/effects/shippdmgbonusmf.py +++ b/eos/effects/shippdmgbonusmf.py @@ -1,11 +1,12 @@ # shipPDmgBonusMF # # Used by: -# Variations of ship: Slasher (3 of 3) # Ship: Cheetah # Ship: Freki # Ship: Republic Fleet Firetail # Ship: Rifter +# Ship: Slasher +# Ship: Stiletto # Ship: Wolf type = "passive" diff --git a/eos/effects/shipprojectilerofmf.py b/eos/effects/shipprojectilerofmf.py new file mode 100644 index 000000000..d4eb0ef4a --- /dev/null +++ b/eos/effects/shipprojectilerofmf.py @@ -0,0 +1,10 @@ +# shipProjectileRofMF +# +# Used by: +# Ship: Claw +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Projectile Turret"), "speed", + src.getModifiedItemAttr("shipBonusMF"), stackingPenalties=True, skill="Minmatar Frigate") diff --git a/eos/effects/shiproledisintegratormaxrangecbc.py b/eos/effects/shiproledisintegratormaxrangecbc.py new file mode 100644 index 000000000..193bcd298 --- /dev/null +++ b/eos/effects/shiproledisintegratormaxrangecbc.py @@ -0,0 +1,10 @@ +# shipRoleDisintegratorMaxRangeCBC +# +# Used by: +# Ship: Drekavac +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Precursor Weapon"), + "maxRange", ship.getModifiedItemAttr("roleBonusCBC")) diff --git a/eos/effects/shipscanresolutionbonusonline.py b/eos/effects/shipscanresolutionbonusonline.py index 041b72b75..989f097a8 100644 --- a/eos/effects/shipscanresolutionbonusonline.py +++ b/eos/effects/shipscanresolutionbonusonline.py @@ -2,6 +2,7 @@ # # Used by: # Modules from group: Signal Amplifier (7 of 7) +# Structure Modules from group: Structure Signal Amplifier (2 of 2) type = "passive" diff --git a/eos/effects/shipsettrackingbonusaf.py b/eos/effects/shipsettrackingbonusaf.py deleted file mode 100644 index f8e952ffc..000000000 --- a/eos/effects/shipsettrackingbonusaf.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), - "trackingSpeed", ship.getModifiedItemAttr("shipBonusAF"), skill="Amarr Frigate") diff --git a/eos/effects/shipxlprojectiledamagerole.py b/eos/effects/shipxlprojectiledamagerole.py deleted file mode 100644 index eaa09058e..000000000 --- a/eos/effects/shipxlprojectiledamagerole.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Projectile Turret"), - "damageMultiplier", ship.getModifiedItemAttr("shipBonusRole7")) diff --git a/eos/effects/siegewarfareshieldcapacitybonusreplacer.py b/eos/effects/siegewarfareshieldcapacitybonusreplacer.py deleted file mode 100644 index 4ec89d183..000000000 --- a/eos/effects/siegewarfareshieldcapacitybonusreplacer.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "gang" -gangBoost = "shieldCapacity" -gangBonus = "shieldCapacityBonus" - - -def handler(fit, container, context): - fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/signatureradiuspreassignment.py b/eos/effects/signatureradiuspreassignment.py deleted file mode 100644 index cb00419db..000000000 --- a/eos/effects/signatureradiuspreassignment.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, module, context): - fit.ship.preAssignItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadius")) diff --git a/eos/effects/skillbombdeploymentmodulereactivationdelaybonus.py b/eos/effects/skillbombdeploymentmodulereactivationdelaybonus.py index e56b2fb94..4db900c9b 100644 --- a/eos/effects/skillbombdeploymentmodulereactivationdelaybonus.py +++ b/eos/effects/skillbombdeploymentmodulereactivationdelaybonus.py @@ -7,4 +7,4 @@ type = "passive" def handler(fit, skill, context): fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Bomb", - "moduleReactivationDelay", skill.getModifiedItemAttr("rofBonus") * skill.level) + "moduleReactivationDelay", skill.getModifiedItemAttr("reactivationDelayBonus") * skill.level) diff --git a/eos/effects/skillbonusdronedurability.py b/eos/effects/skillbonusdronedurability.py index 1023c8412..f36e82a3f 100644 --- a/eos/effects/skillbonusdronedurability.py +++ b/eos/effects/skillbonusdronedurability.py @@ -6,7 +6,7 @@ type = "passive" def handler(fit, src, context): - lvl = src.level + lvl = src.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "hp", src.getModifiedItemAttr("hullHpBonus") * lvl) fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorHP", diff --git a/eos/effects/skillbonusdronedurabilitynotfighters.py b/eos/effects/skillbonusdronedurabilitynotfighters.py new file mode 100644 index 000000000..d2b8a0967 --- /dev/null +++ b/eos/effects/skillbonusdronedurabilitynotfighters.py @@ -0,0 +1,14 @@ +# skillBonusDroneDurabilityNotFighters +# +# Used by: +# Implants from group: Cyber Drones (4 of 4) +type = "passive" + + +def handler(fit, src, context): + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "hp", + src.getModifiedItemAttr("hullHpBonus")) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorHP", + src.getModifiedItemAttr("armorHpBonus")) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "shieldCapacity", + src.getModifiedItemAttr("shieldCapacityBonus")) diff --git a/eos/effects/skillbonusdroneinterfacing.py b/eos/effects/skillbonusdroneinterfacing.py index 4aec63e83..d832d432b 100644 --- a/eos/effects/skillbonusdroneinterfacing.py +++ b/eos/effects/skillbonusdroneinterfacing.py @@ -6,7 +6,7 @@ type = "passive" def handler(fit, src, context): - lvl = src.level + lvl = src.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "damageMultiplier", src.getModifiedItemAttr("damageMultiplierBonus") * lvl) fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), diff --git a/eos/effects/skillbonusdroneinterfacingnotfighters.py b/eos/effects/skillbonusdroneinterfacingnotfighters.py new file mode 100644 index 000000000..01ab85de7 --- /dev/null +++ b/eos/effects/skillbonusdroneinterfacingnotfighters.py @@ -0,0 +1,11 @@ +# skillBonusDroneInterfacingNotFighters +# +# Used by: +# Implant: CreoDron 'Bumblebee' Drone Tuner T10-5D +# Implant: CreoDron 'Yellowjacket' Drone Tuner D5-10T +type = "passive" + + +def handler(fit, src, context): + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "damageMultiplier", + src.getModifiedItemAttr("damageMultiplierBonus")) diff --git a/eos/effects/skillbonusdronenavigation.py b/eos/effects/skillbonusdronenavigation.py index 01453e891..4f5dd8bc5 100644 --- a/eos/effects/skillbonusdronenavigation.py +++ b/eos/effects/skillbonusdronenavigation.py @@ -6,7 +6,7 @@ type = "passive" def handler(fit, src, context): - lvl = src.level + lvl = src.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "maxVelocity", diff --git a/eos/effects/skillbonusdronesharpshooting.py b/eos/effects/skillbonusdronesharpshooting.py index 29654615c..ea0738dc1 100644 --- a/eos/effects/skillbonusdronesharpshooting.py +++ b/eos/effects/skillbonusdronesharpshooting.py @@ -6,7 +6,7 @@ type = "passive" def handler(fit, src, context): - lvl = src.level + lvl = src.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "maxRange", src.getModifiedItemAttr("rangeSkillBonus") * lvl) fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "fighterAbilityMissilesRange", diff --git a/eos/effects/skirmishwarfareagilitybonusreplacer.py b/eos/effects/skirmishwarfareagilitybonusreplacer.py deleted file mode 100644 index 75aff212a..000000000 --- a/eos/effects/skirmishwarfareagilitybonusreplacer.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "gang" -gangBoost = "agility" -gangBonus = "agilityBonus" - - -def handler(fit, container, context): - fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/smalldisintegratormaxrangebonus.py b/eos/effects/smalldisintegratormaxrangebonus.py new file mode 100644 index 000000000..74e4dca9d --- /dev/null +++ b/eos/effects/smalldisintegratormaxrangebonus.py @@ -0,0 +1,10 @@ +# smallDisintegratorMaxRangeBonus +# +# Used by: +# Ship: Kikimora +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "maxRange", ship.getModifiedItemAttr("maxRangeBonus")) diff --git a/eos/effects/smalldisintegratorskilldmgbonus.py b/eos/effects/smalldisintegratorskilldmgbonus.py new file mode 100644 index 000000000..8a3b7babe --- /dev/null +++ b/eos/effects/smalldisintegratorskilldmgbonus.py @@ -0,0 +1,11 @@ +# smallDisintegratorSkillDmgBonus +# +# Used by: +# Skill: Small Disintegrator Specialization +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Disintegrator Specialization"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/smallprecursorturretdmgbonusrequiredskill.py b/eos/effects/smallprecursorturretdmgbonusrequiredskill.py new file mode 100644 index 000000000..1d3fbe176 --- /dev/null +++ b/eos/effects/smallprecursorturretdmgbonusrequiredskill.py @@ -0,0 +1,11 @@ +# smallPrecursorTurretDmgBonusRequiredSkill +# +# Used by: +# Skill: Small Precursor Weapon +type = "passive" + + +def handler(fit, container, context): + level = container.level if "skill" in context else 1 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "damageMultiplier", container.getModifiedItemAttr("damageMultiplierBonus") * level) diff --git a/eos/effects/stripminerdurationmultiplier.py b/eos/effects/stripminerdurationmultiplier.py new file mode 100644 index 000000000..c9d0179a6 --- /dev/null +++ b/eos/effects/stripminerdurationmultiplier.py @@ -0,0 +1,10 @@ +# stripMinerDurationMultiplier +# +# Used by: +# Module: Frostline 'Omnivore' Harvester Upgrade +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Strip Miner", + "duration", module.getModifiedItemAttr("miningDurationMultiplier")) diff --git a/eos/effects/structuralanalysiseffect.py b/eos/effects/structuralanalysiseffect.py index 4bd171488..b94832ca0 100644 --- a/eos/effects/structuralanalysiseffect.py +++ b/eos/effects/structuralanalysiseffect.py @@ -1,7 +1,6 @@ # structuralAnalysisEffect # # Used by: -# Implants named like: Agency 'Hardshell' TB Dose (3 of 3) # Implants named like: Inherent Implants 'Noble' Repair Proficiency RP (6 of 6) # Modules named like: Auxiliary Nano Pump (8 of 8) # Implant: Imperial Navy Modified 'Noble' Implant diff --git a/eos/effects/structureaoerofrolebonus.py b/eos/effects/structureaoerofrolebonus.py index 78b3205ab..48dc00c7d 100644 --- a/eos/effects/structureaoerofrolebonus.py +++ b/eos/effects/structureaoerofrolebonus.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureAoERoFRoleBonus +# +# Used by: +# Items from category: Structure (11 of 17) +# Structures from group: Citadel (8 of 9) type = "passive" diff --git a/eos/effects/structurearmorhpbonus.py b/eos/effects/structurearmorhpbonus.py new file mode 100644 index 000000000..9c2c727ae --- /dev/null +++ b/eos/effects/structurearmorhpbonus.py @@ -0,0 +1,10 @@ +# structureArmorHPBonus +# +# Used by: +# Structure Modules from group: Structure Armor Reinforcer (2 of 2) +type = "passive" +runTime = "early" + + +def handler(fit, src, context): + fit.ship.boostItemAttr("hiddenArmorHPMultiplier", src.getModifiedItemAttr("armorHpBonus"), stackingPenalties=True) diff --git a/eos/effects/structurearmorhpmultiply.py b/eos/effects/structurearmorhpmultiply.py deleted file mode 100644 index 1e34232ed..000000000 --- a/eos/effects/structurearmorhpmultiply.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, src, context): - fit.ship.multiplyItemAttr("hiddenArmorHPMultiplier", src.getModifiedItemAttr("armorHPMultiplier")) diff --git a/eos/effects/structureballisticcontrolsystem.py b/eos/effects/structureballisticcontrolsystem.py index 8a167b6be..bce3a7a74 100644 --- a/eos/effects/structureballisticcontrolsystem.py +++ b/eos/effects/structureballisticcontrolsystem.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureBallisticControlSystem +# +# Used by: +# Variations of structure module: Standup Ballistic Control System I (2 of 2) type = "passive" diff --git a/eos/effects/structurecapacitorcapacitybonus.py b/eos/effects/structurecapacitorcapacitybonus.py index df4384f5b..b0477fe60 100644 --- a/eos/effects/structurecapacitorcapacitybonus.py +++ b/eos/effects/structurecapacitorcapacitybonus.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureCapacitorCapacityBonus +# +# Used by: +# Structure Modules from group: Structure Capacitor Battery (2 of 2) type = "passive" diff --git a/eos/effects/structureenergyneutralizerfalloff.py b/eos/effects/structureenergyneutralizerfalloff.py index 02ee9b4f8..c9cdd769e 100644 --- a/eos/effects/structureenergyneutralizerfalloff.py +++ b/eos/effects/structureenergyneutralizerfalloff.py @@ -1,13 +1,22 @@ -# Not used by any item +# structureEnergyNeutralizerFalloff +# +# Used by: +# Structure Modules from group: Structure Energy Neutralizer (5 of 5) from eos.saveddata.module import State +from eos.modifiedAttributeDict import ModifiedAttributeDict type = "active", "projected" -def handler(fit, container, context): +def handler(fit, src, context, **kwargs): amount = 0 if "projected" in context: - if (hasattr(container, "state") and container.state >= State.ACTIVE) or hasattr(container, "amountActive"): - amount = container.getModifiedItemAttr("energyNeutralizerAmount") - time = container.getModifiedItemAttr("duration") - fit.addDrain(container, time, amount, 0) + if (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive"): + amount = src.getModifiedItemAttr("energyNeutralizerAmount") + + if 'effect' in kwargs: + amount *= ModifiedAttributeDict.getResistance(fit, kwargs['effect']) + + time = src.getModifiedItemAttr("duration") + + fit.addDrain(src, time, amount, 0) \ No newline at end of file diff --git a/eos/effects/structurefullpowerstatehitpointmodifier.py b/eos/effects/structurefullpowerstatehitpointmodifier.py index 54f9ce7b6..5ae185d64 100644 --- a/eos/effects/structurefullpowerstatehitpointmodifier.py +++ b/eos/effects/structurefullpowerstatehitpointmodifier.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureFullPowerStateHitpointModifier +# +# Used by: +# Items from category: Structure (17 of 17) type = "passive" diff --git a/eos/effects/structurehiddenarmorhpmultiplier.py b/eos/effects/structurehiddenarmorhpmultiplier.py index 09e2d31c7..af1b39be3 100644 --- a/eos/effects/structurehiddenarmorhpmultiplier.py +++ b/eos/effects/structurehiddenarmorhpmultiplier.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureHiddenArmorHPMultiplier +# +# Used by: +# Items from category: Structure (17 of 17) type = "passive" diff --git a/eos/effects/structurehiddenmissiledamagemultiplier.py b/eos/effects/structurehiddenmissiledamagemultiplier.py index bd8d79366..61abbf2f4 100644 --- a/eos/effects/structurehiddenmissiledamagemultiplier.py +++ b/eos/effects/structurehiddenmissiledamagemultiplier.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureHiddenMissileDamageMultiplier +# +# Used by: +# Items from category: Structure (14 of 17) type = "passive" diff --git a/eos/effects/structuremissileguidanceenhancer.py b/eos/effects/structuremissileguidanceenhancer.py index 9c0ce2515..59686c5e1 100644 --- a/eos/effects/structuremissileguidanceenhancer.py +++ b/eos/effects/structuremissileguidanceenhancer.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureMissileGuidanceEnhancer +# +# Used by: +# Variations of structure module: Standup Missile Guidance Enhancer I (2 of 2) type = "passive" diff --git a/eos/effects/structuremodifypowerrechargerate.py b/eos/effects/structuremodifypowerrechargerate.py index ed8e9c074..f5977b6fe 100644 --- a/eos/effects/structuremodifypowerrechargerate.py +++ b/eos/effects/structuremodifypowerrechargerate.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModifyPowerRechargeRate +# +# Used by: +# Structure Modules from group: Structure Capacitor Power Relay (2 of 2) type = "passive" diff --git a/eos/effects/structuremoduleeffectecm.py b/eos/effects/structuremoduleeffectecm.py index 9a39b452a..a2725fb5e 100644 --- a/eos/effects/structuremoduleeffectecm.py +++ b/eos/effects/structuremoduleeffectecm.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModuleEffectECM +# +# Used by: +# Structure Modules from group: Structure ECM Battery (3 of 3) type = "projected", "active" diff --git a/eos/effects/structuremoduleeffectremotesensordampener.py b/eos/effects/structuremoduleeffectremotesensordampener.py index 08aa61e57..23cd49378 100644 --- a/eos/effects/structuremoduleeffectremotesensordampener.py +++ b/eos/effects/structuremoduleeffectremotesensordampener.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModuleEffectRemoteSensorDampener +# +# Used by: +# Variations of structure module: Standup Remote Sensor Dampener I (2 of 2) type = "projected", "active" diff --git a/eos/effects/structuremoduleeffectstasiswebifier.py b/eos/effects/structuremoduleeffectstasiswebifier.py index aa0b0d553..30040db35 100644 --- a/eos/effects/structuremoduleeffectstasiswebifier.py +++ b/eos/effects/structuremoduleeffectstasiswebifier.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModuleEffectStasisWebifier +# +# Used by: +# Structure Modules from group: Structure Stasis Webifier (2 of 2) type = "active", "projected" diff --git a/eos/effects/structuremoduleeffecttargetpainter.py b/eos/effects/structuremoduleeffecttargetpainter.py index 7c15140e3..d89abe4b3 100644 --- a/eos/effects/structuremoduleeffecttargetpainter.py +++ b/eos/effects/structuremoduleeffecttargetpainter.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModuleEffectTargetPainter +# +# Used by: +# Variations of structure module: Standup Target Painter I (2 of 2) type = "projected", "active" diff --git a/eos/effects/structuremoduleeffectweapondisruption.py b/eos/effects/structuremoduleeffectweapondisruption.py index 3b6365e37..ff3f705ab 100644 --- a/eos/effects/structuremoduleeffectweapondisruption.py +++ b/eos/effects/structuremoduleeffectweapondisruption.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureModuleEffectWeaponDisruption +# +# Used by: +# Variations of structure module: Standup Weapon Disruptor I (2 of 2) type = "active", "projected" diff --git a/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py b/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py index 6d4503ec7..11045a7c2 100644 --- a/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py +++ b/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py @@ -1,10 +1,13 @@ -# Not used by any item +# structureRigAoeVelocityBonusSingleTargetMissiles +# +# Used by: +# Structure Modules named like: Standup Set Missile (6 of 8) type = "passive" def handler(fit, src, context): groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile") - fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups, + fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in groups, "aoeVelocity", src.getModifiedItemAttr("structureRigMissileExploVeloBonus"), stackingPenalties=True) diff --git a/eos/effects/structurerigdoomsdaydamageloss.py b/eos/effects/structurerigdoomsdaydamageloss.py index e2c6c613b..f81301110 100644 --- a/eos/effects/structurerigdoomsdaydamageloss.py +++ b/eos/effects/structurerigdoomsdaydamageloss.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureRigDoomsdayDamageLoss +# +# Used by: +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigdoomsdaytargetamountbonus.py b/eos/effects/structurerigdoomsdaytargetamountbonus.py index a68ca1054..ece92271a 100644 --- a/eos/effects/structurerigdoomsdaytargetamountbonus.py +++ b/eos/effects/structurerigdoomsdaytargetamountbonus.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureRigDoomsdayTargetAmountBonus +# +# Used by: +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigewcapacitorneed.py b/eos/effects/structurerigewcapacitorneed.py index dfeae5753..caaacb6ea 100644 --- a/eos/effects/structurerigewcapacitorneed.py +++ b/eos/effects/structurerigewcapacitorneed.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigEWCapacitorNeed +# +# Used by: +# Structure Modules from group: Structure Combat Rig M - EW Cap Reduction (2 of 2) +# Structure Modules named like: Standup Set EW (4 of 4) type = "passive" diff --git a/eos/effects/structurerigewmaxrangefalloff.py b/eos/effects/structurerigewmaxrangefalloff.py index fa6ed13c9..679e4c29e 100644 --- a/eos/effects/structurerigewmaxrangefalloff.py +++ b/eos/effects/structurerigewmaxrangefalloff.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigEWMaxRangeFalloff +# +# Used by: +# Structure Modules from group: Structure Combat Rig M - EW projection (2 of 2) +# Structure Modules named like: Standup Set EW (4 of 4) type = "passive" diff --git a/eos/effects/structurerigexplosionradiusbonusaoemissiles.py b/eos/effects/structurerigexplosionradiusbonusaoemissiles.py index 34e840638..a6fedf168 100644 --- a/eos/effects/structurerigexplosionradiusbonusaoemissiles.py +++ b/eos/effects/structurerigexplosionradiusbonusaoemissiles.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigExplosionRadiusBonusAoEMissiles +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - AoE Launcher Application and Projection (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Missile and AoE Missile (2 of 2) type = "passive" diff --git a/eos/effects/structurerigmaxtargetrange.py b/eos/effects/structurerigmaxtargetrange.py index 95fee8a55..52ac17bc8 100644 --- a/eos/effects/structurerigmaxtargetrange.py +++ b/eos/effects/structurerigmaxtargetrange.py @@ -1,4 +1,9 @@ -# Not used by any item +# structureRigMaxTargetRange +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - Max Targets and Sensor Boosting (2 of 2) +# Structure Modules from group: Structure Combat Rig M - Boosted Sensors (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigmaxtargets.py b/eos/effects/structurerigmaxtargets.py index 47e2ce5d2..452af580d 100644 --- a/eos/effects/structurerigmaxtargets.py +++ b/eos/effects/structurerigmaxtargets.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigMaxTargets +# +# Used by: +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) +# Structure Modules named like: Standup Set Target (4 of 4) type = "passive" diff --git a/eos/effects/structurerigneutralizercapacitorneed.py b/eos/effects/structurerigneutralizercapacitorneed.py index c570161fa..89a2ffec0 100644 --- a/eos/effects/structurerigneutralizercapacitorneed.py +++ b/eos/effects/structurerigneutralizercapacitorneed.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigNeutralizerCapacitorNeed +# +# Used by: +# Structure Modules from group: Structure Combat Rig XL - Energy Neutralizer and EW (2 of 2) +# Structure Modules named like: Standup Set Energy Neutralizer (4 of 6) type = "passive" diff --git a/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py b/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py index d123e0278..55a835155 100644 --- a/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py +++ b/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigNeutralizerMaxRangeFalloffEffectiveness +# +# Used by: +# Structure Modules from group: Structure Combat Rig XL - Energy Neutralizer and EW (2 of 2) +# Structure Modules named like: Standup Set Energy Neutralizer (4 of 6) type = "passive" diff --git a/eos/effects/structurerigpdbcapacitorneed.py b/eos/effects/structurerigpdbcapacitorneed.py index a32e63ecc..eb1ee6167 100644 --- a/eos/effects/structurerigpdbcapacitorneed.py +++ b/eos/effects/structurerigpdbcapacitorneed.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigPDBCapacitorNeed +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - Point Defense Battery Application and Projection (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigpdbmaxrange.py b/eos/effects/structurerigpdbmaxrange.py index 64994bf26..5373add73 100644 --- a/eos/effects/structurerigpdbmaxrange.py +++ b/eos/effects/structurerigpdbmaxrange.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigPDBmaxRange +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - Point Defense Battery Application and Projection (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigsensorresolution.py b/eos/effects/structurerigsensorresolution.py index 8d68050e6..26a8ebd58 100644 --- a/eos/effects/structurerigsensorresolution.py +++ b/eos/effects/structurerigsensorresolution.py @@ -1,4 +1,9 @@ -# Not used by any item +# structureRigSensorResolution +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - Max Targets and Sensor Boosting (2 of 2) +# Structure Modules from group: Structure Combat Rig M - Boosted Sensors (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Doomsday and Targeting (2 of 2) type = "passive" diff --git a/eos/effects/structurerigvelocitybonusaoemissiles.py b/eos/effects/structurerigvelocitybonusaoemissiles.py index 636134b1f..38985d031 100644 --- a/eos/effects/structurerigvelocitybonusaoemissiles.py +++ b/eos/effects/structurerigvelocitybonusaoemissiles.py @@ -1,4 +1,8 @@ -# Not used by any item +# structureRigVelocityBonusAoeMissiles +# +# Used by: +# Structure Modules from group: Structure Combat Rig L - AoE Launcher Application and Projection (2 of 2) +# Structure Modules from group: Structure Combat Rig XL - Missile and AoE Missile (2 of 2) type = "passive" diff --git a/eos/effects/structurerigvelocitybonussingletargetmissiles.py b/eos/effects/structurerigvelocitybonussingletargetmissiles.py index 1af82afd0..51103c3a8 100644 --- a/eos/effects/structurerigvelocitybonussingletargetmissiles.py +++ b/eos/effects/structurerigvelocitybonussingletargetmissiles.py @@ -1,9 +1,12 @@ -# Not used by any item +# structureRigVelocityBonusSingleTargetMissiles +# +# Used by: +# Structure Modules named like: Standup Set Missile (6 of 8) type = "passive" def handler(fit, src, context): groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile") - fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups, + fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in groups, "maxVelocity", src.getModifiedItemAttr("structureRigMissileVelocityBonus"), stackingPenalties=True) diff --git a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py index aee7b22f9..e6ab9536e 100644 --- a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py @@ -1,4 +1,7 @@ -# Not used by any item +# structureWarpScrambleBlockMWDWithNPCEffect +# +# Used by: +# Structure Modules from group: Structure Warp Scrambler (2 of 2) from eos.saveddata.module import State # Not used by any item diff --git a/eos/effects/subsystembonusamarrdefensive2armorrepheat.py b/eos/effects/subsystembonusamarrdefensive2armorrepheat.py deleted file mode 100644 index b8d62df63..000000000 --- a/eos/effects/subsystembonusamarrdefensive2armorrepheat.py +++ /dev/null @@ -1,12 +0,0 @@ -# subsystemBonusAmarrDefensive2ArmorRepHeat -# -# Used by: -# Subsystem: Legion Defensive - Nanobot Injector -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), "overloadArmorDamageAmount", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), "overloadSelfDurationBonus", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py deleted file mode 100644 index 1c092a9ff..000000000 --- a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), - "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py b/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py deleted file mode 100644 index ca2480638..000000000 --- a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensivearmorresistance.py b/eos/effects/subsystembonusamarrdefensivearmorresistance.py deleted file mode 100644 index ea42d6a15..000000000 --- a/eos/effects/subsystembonusamarrdefensivearmorresistance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.boostItemAttr("armor{0}DamageResonance".format(type), - module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py deleted file mode 100644 index 5f8a91832..000000000 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py deleted file mode 100644 index 7dd70fefa..000000000 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensivescanprobestrength2.py b/eos/effects/subsystembonusamarrdefensivescanprobestrength2.py deleted file mode 100644 index 7f4ced1f8..000000000 --- a/eos/effects/subsystembonusamarrdefensivescanprobestrength2.py +++ /dev/null @@ -1,11 +0,0 @@ -# subSystemBonusAmarrDefensiveScanProbeStrength2 -# -# Used by: -# Subsystem: Legion Defensive - Covert Reconfiguration -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Astrometrics"), - "baseSensorStrength", module.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py b/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py deleted file mode 100644 index 34c780376..000000000 --- a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensivetractorbeambonus3.py b/eos/effects/subsystembonusamarrdefensivetractorbeambonus3.py deleted file mode 100644 index f2df1979b..000000000 --- a/eos/effects/subsystembonusamarrdefensivetractorbeambonus3.py +++ /dev/null @@ -1,15 +0,0 @@ -# subSystemBonusAmarrDefensiveTractorBeamBonus3 -# -# Used by: -# Subsystem: Legion Defensive - Covert Reconfiguration -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", module.getModifiedItemAttr("subsystemBonusAmarrDefensive3"), - skill="Amarr Defensive Systems") - - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", module.getModifiedItemAttr("subsystemBonusAmarrDefensive3"), - skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrelectronic2maxtargetingrange.py b/eos/effects/subsystembonusamarrelectronic2maxtargetingrange.py deleted file mode 100644 index 5ae759435..000000000 --- a/eos/effects/subsystembonusamarrelectronic2maxtargetingrange.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("subsystemBonusAmarrElectronic2"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronic2scanresolution.py b/eos/effects/subsystembonusamarrelectronic2scanresolution.py deleted file mode 100644 index f62230dac..000000000 --- a/eos/effects/subsystembonusamarrelectronic2scanresolution.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("subsystemBonusAmarrElectronic2"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronic2tractorbeamrange.py b/eos/effects/subsystembonusamarrelectronic2tractorbeamrange.py deleted file mode 100644 index 2e49bfe9d..000000000 --- a/eos/effects/subsystembonusamarrelectronic2tractorbeamrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", module.getModifiedItemAttr("subsystemBonusAmarrElectronic2"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronic2tractorbeamvelocity.py b/eos/effects/subsystembonusamarrelectronic2tractorbeamvelocity.py deleted file mode 100644 index 0af403adc..000000000 --- a/eos/effects/subsystembonusamarrelectronic2tractorbeamvelocity.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", module.getModifiedItemAttr("subsystemBonusAmarrElectronic2"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronicenergydestabilizeramount.py b/eos/effects/subsystembonusamarrelectronicenergydestabilizeramount.py deleted file mode 100644 index e40b2fef6..000000000 --- a/eos/effects/subsystembonusamarrelectronicenergydestabilizeramount.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Neutralizer", - "energyNeutralizerAmount", - module.getModifiedItemAttr("subsystemBonusAmarrElectronic"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronicenergyvampireamount.py b/eos/effects/subsystembonusamarrelectronicenergyvampireamount.py deleted file mode 100644 index e6723b196..000000000 --- a/eos/effects/subsystembonusamarrelectronicenergyvampireamount.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", - "powerTransferAmount", module.getModifiedItemAttr("subsystemBonusAmarrElectronic"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronicscanprobestrength.py b/eos/effects/subsystembonusamarrelectronicscanprobestrength.py deleted file mode 100644 index 8b8e5ee61..000000000 --- a/eos/effects/subsystembonusamarrelectronicscanprobestrength.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Scanner Probe", - "baseSensorStrength", module.getModifiedItemAttr("subsystemBonusAmarrElectronic"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrelectronicscanstrengthradar.py b/eos/effects/subsystembonusamarrelectronicscanstrengthradar.py deleted file mode 100644 index e0b13d37a..000000000 --- a/eos/effects/subsystembonusamarrelectronicscanstrengthradar.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanRadarStrength", module.getModifiedItemAttr("subsystemBonusAmarrElectronic"), - skill="Amarr Electronic Systems") diff --git a/eos/effects/subsystembonusamarrengineeringcapacitorcapacity.py b/eos/effects/subsystembonusamarrengineeringcapacitorcapacity.py deleted file mode 100644 index 22e8f4d41..000000000 --- a/eos/effects/subsystembonusamarrengineeringcapacitorcapacity.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("capacitorCapacity", module.getModifiedItemAttr("subsystemBonusAmarrCore"), - skill="Amarr Core Systems") diff --git a/eos/effects/subsystembonusamarrengineeringcapacitorrecharge.py b/eos/effects/subsystembonusamarrengineeringcapacitorrecharge.py deleted file mode 100644 index 5471853cd..000000000 --- a/eos/effects/subsystembonusamarrengineeringcapacitorrecharge.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("rechargeRate", module.getModifiedItemAttr("subsystemBonusAmarrCore"), - skill="Amarr Core Systems") diff --git a/eos/effects/subsystembonusamarrengineeringpoweroutput.py b/eos/effects/subsystembonusamarrengineeringpoweroutput.py deleted file mode 100644 index 6ab9a9ad3..000000000 --- a/eos/effects/subsystembonusamarrengineeringpoweroutput.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("powerOutput", module.getModifiedItemAttr("subsystemBonusAmarrCore"), - skill="Amarr Core Systems") diff --git a/eos/effects/subsystembonusamarroffensive2hamemdamage.py b/eos/effects/subsystembonusamarroffensive2hamemdamage.py deleted file mode 100644 index efc2542a7..000000000 --- a/eos/effects/subsystembonusamarroffensive2hamemdamage.py +++ /dev/null @@ -1,11 +0,0 @@ -# subsystemBonusAmarrOffensive2HAMEmDamage -# -# Used by: -# Subsystem: Legion Offensive - Assault Optimization -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), - "emDamage", module.getModifiedItemAttr("subsystemBonusAmarrOffensive2"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensive2hamexplosivedamage.py b/eos/effects/subsystembonusamarroffensive2hamexplosivedamage.py deleted file mode 100644 index 892f1d13f..000000000 --- a/eos/effects/subsystembonusamarroffensive2hamexplosivedamage.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), - "explosiveDamage", module.getModifiedItemAttr("subsystemBonusAmarrOffensive2"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensive2hamkineticdamage.py b/eos/effects/subsystembonusamarroffensive2hamkineticdamage.py deleted file mode 100644 index c638eea08..000000000 --- a/eos/effects/subsystembonusamarroffensive2hamkineticdamage.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), - "kineticDamage", module.getModifiedItemAttr("subsystemBonusAmarrOffensive2"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensive2hamthermaldamage.py b/eos/effects/subsystembonusamarroffensive2hamthermaldamage.py deleted file mode 100644 index e456df6ff..000000000 --- a/eos/effects/subsystembonusamarroffensive2hamthermaldamage.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), - "thermalDamage", module.getModifiedItemAttr("subsystemBonusAmarrOffensive2"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensive3dronehp.py b/eos/effects/subsystembonusamarroffensive3dronehp.py deleted file mode 100644 index 5160060df..000000000 --- a/eos/effects/subsystembonusamarroffensive3dronehp.py +++ /dev/null @@ -1,12 +0,0 @@ -# subsystemBonusAmarrOffensive3DroneHP -# -# Used by: -# Subsystem: Legion Offensive - Drone Synthesis Projector -type = "passive" - - -def handler(fit, module, context): - for layer in ("shieldCapacity", "armorHP", "hp"): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), layer, - module.getModifiedItemAttr("subsystemBonusAmarrOffensive3"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensiveassaultmissilelauncherrof.py b/eos/effects/subsystembonusamarroffensiveassaultmissilelauncherrof.py deleted file mode 100644 index fe66e0d40..000000000 --- a/eos/effects/subsystembonusamarroffensiveassaultmissilelauncherrof.py +++ /dev/null @@ -1,11 +0,0 @@ -# subsystemBonusAmarrOffensiveAssaultMissileLauncherROF -# -# Used by: -# Subsystem: Legion Offensive - Assault Optimization -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Rapid Light", - "speed", module.getModifiedItemAttr("subsystemBonusAmarrOffensive"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensivedronedamagemultiplier.py b/eos/effects/subsystembonusamarroffensivedronedamagemultiplier.py deleted file mode 100644 index 40f371fd4..000000000 --- a/eos/effects/subsystembonusamarroffensivedronedamagemultiplier.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "damageMultiplier", module.getModifiedItemAttr("subsystemBonusAmarrOffensive"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensiveenergyweaponcapacitorneed.py b/eos/effects/subsystembonusamarroffensiveenergyweaponcapacitorneed.py deleted file mode 100644 index 51a9c7809..000000000 --- a/eos/effects/subsystembonusamarroffensiveenergyweaponcapacitorneed.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Energy Turret"), - "capacitorNeed", module.getModifiedItemAttr("subsystemBonusAmarrOffensive"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensiveheavyassaultmissilelauncherrof.py b/eos/effects/subsystembonusamarroffensiveheavyassaultmissilelauncherrof.py deleted file mode 100644 index 52440263a..000000000 --- a/eos/effects/subsystembonusamarroffensiveheavyassaultmissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy Assault", - "speed", module.getModifiedItemAttr("subsystemBonusAmarrOffensive"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarroffensiveheavymissilelauncherrof.py b/eos/effects/subsystembonusamarroffensiveheavymissilelauncherrof.py deleted file mode 100644 index c0db27ad2..000000000 --- a/eos/effects/subsystembonusamarroffensiveheavymissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy", - "speed", module.getModifiedItemAttr("subsystemBonusAmarrOffensive"), - skill="Amarr Offensive Systems") diff --git a/eos/effects/subsystembonusamarrpropulsionagility.py b/eos/effects/subsystembonusamarrpropulsionagility.py deleted file mode 100644 index a8d84bdba..000000000 --- a/eos/effects/subsystembonusamarrpropulsionagility.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("subsystemBonusAmarrPropulsion"), - skill="Amarr Propulsion Systems") diff --git a/eos/effects/subsystembonusamarrpropulsionmwdpenalty.py b/eos/effects/subsystembonusamarrpropulsionmwdpenalty.py deleted file mode 100644 index cc907e436..000000000 --- a/eos/effects/subsystembonusamarrpropulsionmwdpenalty.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), - "signatureRadiusBonus", module.getModifiedItemAttr("subsystemBonusAmarrPropulsion"), - skill="Amarr Propulsion Systems") diff --git a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py deleted file mode 100644 index 5835257fa..000000000 --- a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), - "shieldBonus", module.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensive2shieldboostheat.py b/eos/effects/subsystembonuscaldaridefensive2shieldboostheat.py deleted file mode 100644 index bd28f05c4..000000000 --- a/eos/effects/subsystembonuscaldaridefensive2shieldboostheat.py +++ /dev/null @@ -1,14 +0,0 @@ -# subsystemBonusCaldariDefensive2ShieldBoostHeat -# -# Used by: -# Subsystem: Tengu Defensive - Amplification Node -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"), - "overloadSelfDurationBonus", src.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"), - "overloadShieldBonus", src.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensive3tractorbeambonus.py b/eos/effects/subsystembonuscaldaridefensive3tractorbeambonus.py deleted file mode 100644 index 9802868e5..000000000 --- a/eos/effects/subsystembonuscaldaridefensive3tractorbeambonus.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", src.getModifiedItemAttr("subsystemBonusCaldariDefensive3"), stackingPenalties=True, skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", src.getModifiedItemAttr("subsystemBonusCaldariDefensive3"), skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py deleted file mode 100644 index a6245c21a..000000000 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py deleted file mode 100644 index 0cf7a3ef8..000000000 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveshieldrechargerate.py b/eos/effects/subsystembonuscaldaridefensiveshieldrechargerate.py deleted file mode 100644 index 7ddb92b06..000000000 --- a/eos/effects/subsystembonuscaldaridefensiveshieldrechargerate.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("shieldRechargeRate", module.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveshieldresistance.py b/eos/effects/subsystembonuscaldaridefensiveshieldresistance.py deleted file mode 100644 index 32dd02f63..000000000 --- a/eos/effects/subsystembonuscaldaridefensiveshieldresistance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.boostItemAttr("shield{0}DamageResonance".format(type), - module.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py b/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py deleted file mode 100644 index 0dbfb904b..000000000 --- a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py b/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py deleted file mode 100644 index 2841c2d14..000000000 --- a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), - skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldarielectronic2maxtargetingrange.py b/eos/effects/subsystembonuscaldarielectronic2maxtargetingrange.py deleted file mode 100644 index dfecf6025..000000000 --- a/eos/effects/subsystembonuscaldarielectronic2maxtargetingrange.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("subsystemBonusCaldariElectronic2"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectronic2tractorbeamrange.py b/eos/effects/subsystembonuscaldarielectronic2tractorbeamrange.py deleted file mode 100644 index dec075218..000000000 --- a/eos/effects/subsystembonuscaldarielectronic2tractorbeamrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", module.getModifiedItemAttr("subsystemBonusCaldariElectronic2"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectronic2tractorbeamvelocity.py b/eos/effects/subsystembonuscaldarielectronic2tractorbeamvelocity.py deleted file mode 100644 index 93b628f94..000000000 --- a/eos/effects/subsystembonuscaldarielectronic2tractorbeamvelocity.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", module.getModifiedItemAttr("subsystemBonusCaldariElectronic2"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectroniccpu.py b/eos/effects/subsystembonuscaldarielectroniccpu.py deleted file mode 100644 index a953001a6..000000000 --- a/eos/effects/subsystembonuscaldarielectroniccpu.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("cpuOutput", module.getModifiedItemAttr("subsystemBonusCaldariElectronic"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectronicecmrange.py b/eos/effects/subsystembonuscaldarielectronicecmrange.py deleted file mode 100644 index 52c4a2514..000000000 --- a/eos/effects/subsystembonuscaldarielectronicecmrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM", - "maxRange", module.getModifiedItemAttr("subsystemBonusCaldariElectronic"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectronicscanprobestrength.py b/eos/effects/subsystembonuscaldarielectronicscanprobestrength.py deleted file mode 100644 index a6678f610..000000000 --- a/eos/effects/subsystembonuscaldarielectronicscanprobestrength.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Scanner Probe", - "baseSensorStrength", module.getModifiedItemAttr("subsystemBonusCaldariElectronic"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldarielectronicscanstrengthgravimetric.py b/eos/effects/subsystembonuscaldarielectronicscanstrengthgravimetric.py deleted file mode 100644 index 76e9e6a5e..000000000 --- a/eos/effects/subsystembonuscaldarielectronicscanstrengthgravimetric.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanGravimetricStrength", module.getModifiedItemAttr("subsystemBonusCaldariElectronic"), - skill="Caldari Electronic Systems") diff --git a/eos/effects/subsystembonuscaldariengineeringcapacitorcapacity.py b/eos/effects/subsystembonuscaldariengineeringcapacitorcapacity.py deleted file mode 100644 index e092ba001..000000000 --- a/eos/effects/subsystembonuscaldariengineeringcapacitorcapacity.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("capacitorCapacity", module.getModifiedItemAttr("subsystemBonusCaldariCore"), - skill="Caldari Core Systems") diff --git a/eos/effects/subsystembonuscaldariengineeringcapacitorrecharge.py b/eos/effects/subsystembonuscaldariengineeringcapacitorrecharge.py deleted file mode 100644 index 98d3a2935..000000000 --- a/eos/effects/subsystembonuscaldariengineeringcapacitorrecharge.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("rechargeRate", module.getModifiedItemAttr("subsystemBonusCaldariCore"), - skill="Caldari Core Systems") diff --git a/eos/effects/subsystembonuscaldariengineeringpoweroutput.py b/eos/effects/subsystembonuscaldariengineeringpoweroutput.py deleted file mode 100644 index 2726e0af2..000000000 --- a/eos/effects/subsystembonuscaldariengineeringpoweroutput.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("powerOutput", module.getModifiedItemAttr("subsystemBonusCaldariCore"), - skill="Caldari Core Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3ewstrengthgrav.py b/eos/effects/subsystembonuscaldarioffensive3ewstrengthgrav.py deleted file mode 100644 index 4f51c23af..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3ewstrengthgrav.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM", - "scanGravimetricStrengthBonus", - module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3ewstrengthladar.py b/eos/effects/subsystembonuscaldarioffensive3ewstrengthladar.py deleted file mode 100644 index 8114c23d8..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3ewstrengthladar.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM", - "scanLadarStrengthBonus", - module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3ewstrengthmagn.py b/eos/effects/subsystembonuscaldarioffensive3ewstrengthmagn.py deleted file mode 100644 index 69f64527f..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3ewstrengthmagn.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM", - "scanMagnetometricStrengthBonus", - module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3ewstrengthradar.py b/eos/effects/subsystembonuscaldarioffensive3ewstrengthradar.py deleted file mode 100644 index 47eb2f29e..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3ewstrengthradar.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM", - "scanRadarStrengthBonus", - module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3heavyassaultmissilevelocity.py b/eos/effects/subsystembonuscaldarioffensive3heavyassaultmissilevelocity.py deleted file mode 100644 index b595798d8..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3heavyassaultmissilevelocity.py +++ /dev/null @@ -1,11 +0,0 @@ -# subsystemBonusCaldariOffensive3HeavyAssaultMissileVelocity -# -# Used by: -# Subsystem: Tengu Offensive - Accelerated Ejection Bay -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Assault Missiles"), - "maxVelocity", module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensive3heavymissilevelocity.py b/eos/effects/subsystembonuscaldarioffensive3heavymissilevelocity.py deleted file mode 100644 index 9f7a98f9d..000000000 --- a/eos/effects/subsystembonuscaldarioffensive3heavymissilevelocity.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Heavy Missiles"), - "maxVelocity", module.getModifiedItemAttr("subsystemBonusCaldariOffensive3"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensiveassaultmissilelauncherrof.py b/eos/effects/subsystembonuscaldarioffensiveassaultmissilelauncherrof.py deleted file mode 100644 index 02d2d3d03..000000000 --- a/eos/effects/subsystembonuscaldarioffensiveassaultmissilelauncherrof.py +++ /dev/null @@ -1,11 +0,0 @@ -# subsystemBonusCaldariOffensiveAssaultMissileLauncherROF -# -# Used by: -# Variations of subsystem: Tengu Offensive - Accelerated Ejection Bay (3 of 4) -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Rapid Light", - "speed", module.getModifiedItemAttr("subsystemBonusCaldariOffensive"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensiveheavyassaultmissilelauncherrof.py b/eos/effects/subsystembonuscaldarioffensiveheavyassaultmissilelauncherrof.py deleted file mode 100644 index b9923a206..000000000 --- a/eos/effects/subsystembonuscaldarioffensiveheavyassaultmissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy Assault", - "speed", module.getModifiedItemAttr("subsystemBonusCaldariOffensive"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldarioffensiveheavymissilelauncherrof.py b/eos/effects/subsystembonuscaldarioffensiveheavymissilelauncherrof.py deleted file mode 100644 index a0c74ee2a..000000000 --- a/eos/effects/subsystembonuscaldarioffensiveheavymissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy", - "speed", module.getModifiedItemAttr("subsystemBonusCaldariOffensive"), - skill="Caldari Offensive Systems") diff --git a/eos/effects/subsystembonuscaldaripropulsion2warpcapacitor2.py b/eos/effects/subsystembonuscaldaripropulsion2warpcapacitor2.py deleted file mode 100644 index b4b8221c3..000000000 --- a/eos/effects/subsystembonuscaldaripropulsion2warpcapacitor2.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("warpCapacitorNeed", module.getModifiedItemAttr("subsystemBonusCaldariPropulsion2"), - skill="Caldari Propulsion Systems") diff --git a/eos/effects/subsystembonuscaldaripropulsionagility.py b/eos/effects/subsystembonuscaldaripropulsionagility.py deleted file mode 100644 index 2bd22b70d..000000000 --- a/eos/effects/subsystembonuscaldaripropulsionagility.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("subsystemBonusCaldariPropulsion"), - skill="Caldari Propulsion Systems") diff --git a/eos/effects/subsystembonuscaldaripropulsionwarpspeed.py b/eos/effects/subsystembonuscaldaripropulsionwarpspeed.py deleted file mode 100644 index cfd450c94..000000000 --- a/eos/effects/subsystembonuscaldaripropulsionwarpspeed.py +++ /dev/null @@ -1,10 +0,0 @@ -# subsystemBonusCaldariPropulsionWarpSpeed -# -# Used by: -# Subsystem: Tengu Propulsion - Chassis Optimization -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("baseWarpSpeed", module.getModifiedItemAttr("subsystemBonusCaldariPropulsion"), - skill="Caldari Propulsion Systems") diff --git a/eos/effects/subsystembonusgallentedefensive2armorrepheat.py b/eos/effects/subsystembonusgallentedefensive2armorrepheat.py deleted file mode 100644 index e35f7cd1c..000000000 --- a/eos/effects/subsystembonusgallentedefensive2armorrepheat.py +++ /dev/null @@ -1,12 +0,0 @@ -# subsystemBonusGallenteDefensive2ArmorRepHeat -# -# Used by: -# Subsystem: Proteus Defensive - Nanobot Injector -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), "overloadArmorDamageAmount", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive2"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), "overloadSelfDurationBonus", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive2"), skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py deleted file mode 100644 index 42f35203d..000000000 --- a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), - "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusGallenteDefensive2"), - skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensive3tractorbeambonus.py b/eos/effects/subsystembonusgallentedefensive3tractorbeambonus.py deleted file mode 100644 index 4493eaa7a..000000000 --- a/eos/effects/subsystembonusgallentedefensive3tractorbeambonus.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive3"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive3"), skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py b/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py deleted file mode 100644 index 8c7e75a9e..000000000 --- a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensivearmorresistance.py b/eos/effects/subsystembonusgallentedefensivearmorresistance.py deleted file mode 100644 index 89c793799..000000000 --- a/eos/effects/subsystembonusgallentedefensivearmorresistance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.boostItemAttr("armor{0}DamageResonance".format(type), - module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py deleted file mode 100644 index 02d96a8a7..000000000 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py deleted file mode 100644 index 19b0cf02e..000000000 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), - "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py b/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py deleted file mode 100644 index e69d3498c..000000000 --- a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallenteelectronic2maxtargetingrange.py b/eos/effects/subsystembonusgallenteelectronic2maxtargetingrange.py deleted file mode 100644 index 5cf5da2da..000000000 --- a/eos/effects/subsystembonusgallenteelectronic2maxtargetingrange.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("subsystemBonusGallenteElectronic2"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectronic2tractorbeamrange.py b/eos/effects/subsystembonusgallenteelectronic2tractorbeamrange.py deleted file mode 100644 index f20787889..000000000 --- a/eos/effects/subsystembonusgallenteelectronic2tractorbeamrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", module.getModifiedItemAttr("subsystemBonusGallenteElectronic2"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectronic2tractorbeamvelocity.py b/eos/effects/subsystembonusgallenteelectronic2tractorbeamvelocity.py deleted file mode 100644 index 179582a9f..000000000 --- a/eos/effects/subsystembonusgallenteelectronic2tractorbeamvelocity.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", module.getModifiedItemAttr("subsystemBonusGallenteElectronic2"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectroniccpu.py b/eos/effects/subsystembonusgallenteelectroniccpu.py deleted file mode 100644 index 2feb00f86..000000000 --- a/eos/effects/subsystembonusgallenteelectroniccpu.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("cpuOutput", module.getModifiedItemAttr("subsystemBonusGallenteElectronic"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectronicscanprobestrength.py b/eos/effects/subsystembonusgallenteelectronicscanprobestrength.py deleted file mode 100644 index 9451a037e..000000000 --- a/eos/effects/subsystembonusgallenteelectronicscanprobestrength.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Scanner Probe", - "baseSensorStrength", - module.getModifiedItemAttr("subsystemBonusGallenteElectronic"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectronicscanstrengthmagnetometric.py b/eos/effects/subsystembonusgallenteelectronicscanstrengthmagnetometric.py deleted file mode 100644 index 34ab8b270..000000000 --- a/eos/effects/subsystembonusgallenteelectronicscanstrengthmagnetometric.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanMagnetometricStrength", module.getModifiedItemAttr("subsystemBonusGallenteElectronic"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteelectronicwarpscramblerange.py b/eos/effects/subsystembonusgallenteelectronicwarpscramblerange.py deleted file mode 100644 index 951c13cea..000000000 --- a/eos/effects/subsystembonusgallenteelectronicwarpscramblerange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Warp Scrambler", - "maxRange", module.getModifiedItemAttr("subsystemBonusGallenteElectronic"), - skill="Gallente Electronic Systems") diff --git a/eos/effects/subsystembonusgallenteengineering2dronemwd.py b/eos/effects/subsystembonusgallenteengineering2dronemwd.py deleted file mode 100644 index e476b9f1d..000000000 --- a/eos/effects/subsystembonusgallenteengineering2dronemwd.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), "maxVelocity", - module.getModifiedItemAttr("subsystemBonusGallenteCore2"), - skill="Gallente Core Systems") diff --git a/eos/effects/subsystembonusgallenteengineeringcapacitorrecharge.py b/eos/effects/subsystembonusgallenteengineeringcapacitorrecharge.py deleted file mode 100644 index 72bb25e12..000000000 --- a/eos/effects/subsystembonusgallenteengineeringcapacitorrecharge.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("rechargeRate", module.getModifiedItemAttr("subsystemBonusGallenteCore"), - skill="Gallente Core Systems") diff --git a/eos/effects/subsystembonusgallenteengineeringdronehp.py b/eos/effects/subsystembonusgallenteengineeringdronehp.py deleted file mode 100644 index f9de09f6e..000000000 --- a/eos/effects/subsystembonusgallenteengineeringdronehp.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for layer in ("shieldCapacity", "armorHP", "hp"): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), layer, - module.getModifiedItemAttr("subsystemBonusGallenteCore"), - skill="Gallente Core Systems") diff --git a/eos/effects/subsystembonusgallenteengineeringpoweroutput.py b/eos/effects/subsystembonusgallenteengineeringpoweroutput.py deleted file mode 100644 index 9bcb7adf3..000000000 --- a/eos/effects/subsystembonusgallenteengineeringpoweroutput.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("powerOutput", module.getModifiedItemAttr("subsystemBonusGallenteCore"), - skill="Gallente Core Systems") diff --git a/eos/effects/subsystembonusgallenteoffensive3dronedamagemultiplier.py b/eos/effects/subsystembonusgallenteoffensive3dronedamagemultiplier.py deleted file mode 100644 index d2eb14e1d..000000000 --- a/eos/effects/subsystembonusgallenteoffensive3dronedamagemultiplier.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "damageMultiplier", module.getModifiedItemAttr("subsystemBonusGallenteOffensive3"), - skill="Gallente Offensive Systems") diff --git a/eos/effects/subsystembonusgallenteoffensivedronehp.py b/eos/effects/subsystembonusgallenteoffensivedronehp.py deleted file mode 100644 index d02eab954..000000000 --- a/eos/effects/subsystembonusgallenteoffensivedronehp.py +++ /dev/null @@ -1,12 +0,0 @@ -# subsystemBonusGallenteOffensiveDroneHP -# -# Used by: -# Subsystem: Proteus Offensive - Drone Synthesis Projector -type = "passive" - - -def handler(fit, module, context): - for layer in ("shieldCapacity", "armorHP", "hp"): - fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), layer, - module.getModifiedItemAttr("subsystemBonusGallenteOffensive"), - skill="Gallente Offensive Systems") diff --git a/eos/effects/subsystembonusgallenteoffensivehybridweapondamagemultiplier.py b/eos/effects/subsystembonusgallenteoffensivehybridweapondamagemultiplier.py deleted file mode 100644 index a94ff5eb0..000000000 --- a/eos/effects/subsystembonusgallenteoffensivehybridweapondamagemultiplier.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Hybrid Turret"), - "damageMultiplier", module.getModifiedItemAttr("subsystemBonusGallenteOffensive"), - skill="Gallente Offensive Systems") diff --git a/eos/effects/subsystembonusgallentepropulsion2warpcapacitor.py b/eos/effects/subsystembonusgallentepropulsion2warpcapacitor.py deleted file mode 100644 index e74f2d22d..000000000 --- a/eos/effects/subsystembonusgallentepropulsion2warpcapacitor.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("warpCapacitorNeed", module.getModifiedItemAttr("subsystemBonusGallentePropulsion2"), - skill="Gallente Propulsion Systems") diff --git a/eos/effects/subsystembonusgallentepropulsionagility.py b/eos/effects/subsystembonusgallentepropulsionagility.py deleted file mode 100644 index e15ff4dee..000000000 --- a/eos/effects/subsystembonusgallentepropulsionagility.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("subsystemBonusGallentePropulsion"), - skill="Gallente Propulsion Systems") diff --git a/eos/effects/subsystembonusgallentepropulsionmwdpenalty.py b/eos/effects/subsystembonusgallentepropulsionmwdpenalty.py deleted file mode 100644 index 98e2a1716..000000000 --- a/eos/effects/subsystembonusgallentepropulsionmwdpenalty.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), - "signatureRadiusBonus", - module.getModifiedItemAttr("subsystemBonusGallentePropulsion"), - skill="Gallente Propulsion Systems") diff --git a/eos/effects/subsystembonusminmatardefensive2localrepheat.py b/eos/effects/subsystembonusminmatardefensive2localrepheat.py deleted file mode 100644 index 732ca1779..000000000 --- a/eos/effects/subsystembonusminmatardefensive2localrepheat.py +++ /dev/null @@ -1,17 +0,0 @@ -# subsystemBonusMinmatarDefensive2LocalRepHeat -# -# Used by: -# Subsystem: Loki Defensive - Adaptive Defense Node -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems") or mod.item.requiresSkill("Shield Operation"), - "overloadSelfDurationBonus", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), - "overloadArmorDamageAmount", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"), - "overloadShieldBonus", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py deleted file mode 100644 index a044b34ae..000000000 --- a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "early" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), - "shieldBonus", module.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensive3tractorbeambonus.py b/eos/effects/subsystembonusminmatardefensive3tractorbeambonus.py deleted file mode 100644 index 46c0dc717..000000000 --- a/eos/effects/subsystembonusminmatardefensive3tractorbeambonus.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive3"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive3"), skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py b/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py deleted file mode 100644 index b0646226d..000000000 --- a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivearmorresistance.py b/eos/effects/subsystembonusminmatardefensivearmorresistance.py deleted file mode 100644 index 155c521eb..000000000 --- a/eos/effects/subsystembonusminmatardefensivearmorresistance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.boostItemAttr("armor{0}DamageResonance".format(type), - module.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensiveshieldresistance.py b/eos/effects/subsystembonusminmatardefensiveshieldresistance.py deleted file mode 100644 index fe65b628c..000000000 --- a/eos/effects/subsystembonusminmatardefensiveshieldresistance.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - for type in ("Em", "Explosive", "Kinetic", "Thermal"): - fit.ship.boostItemAttr("shield{0}DamageResonance".format(type), - module.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py b/eos/effects/subsystembonusminmatardefensivesiegewarfare.py deleted file mode 100644 index c4fcca1fa..000000000 --- a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivesignatureradius.py b/eos/effects/subsystembonusminmatardefensivesignatureradius.py deleted file mode 100644 index c31b4186f..000000000 --- a/eos/effects/subsystembonusminmatardefensivesignatureradius.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py b/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py deleted file mode 100644 index bd9b91cb1..000000000 --- a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py +++ /dev/null @@ -1,20 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", - src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), - skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatarelectronic2maxtargetingrange.py b/eos/effects/subsystembonusminmatarelectronic2maxtargetingrange.py deleted file mode 100644 index 5328c7bb9..000000000 --- a/eos/effects/subsystembonusminmatarelectronic2maxtargetingrange.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic2"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronic2scanresolution.py b/eos/effects/subsystembonusminmatarelectronic2scanresolution.py deleted file mode 100644 index 065af593b..000000000 --- a/eos/effects/subsystembonusminmatarelectronic2scanresolution.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic2"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronic2tractorbeamrange.py b/eos/effects/subsystembonusminmatarelectronic2tractorbeamrange.py deleted file mode 100644 index 2204f35dc..000000000 --- a/eos/effects/subsystembonusminmatarelectronic2tractorbeamrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxRange", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic2"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronic2tractorbeamvelocity.py b/eos/effects/subsystembonusminmatarelectronic2tractorbeamvelocity.py deleted file mode 100644 index f647d5b3c..000000000 --- a/eos/effects/subsystembonusminmatarelectronic2tractorbeamvelocity.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", - "maxTractorVelocity", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic2"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronicscanprobestrength.py b/eos/effects/subsystembonusminmatarelectronicscanprobestrength.py deleted file mode 100644 index 873aa1eca..000000000 --- a/eos/effects/subsystembonusminmatarelectronicscanprobestrength.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Scanner Probe", - "baseSensorStrength", - module.getModifiedItemAttr("subsystemBonusMinmatarElectronic"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronicscanstrengthladar.py b/eos/effects/subsystembonusminmatarelectronicscanstrengthladar.py deleted file mode 100644 index 531b98d96..000000000 --- a/eos/effects/subsystembonusminmatarelectronicscanstrengthladar.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("scanLadarStrength", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarelectronicstasiswebifierrange.py b/eos/effects/subsystembonusminmatarelectronicstasiswebifierrange.py deleted file mode 100644 index 53f0a94aa..000000000 --- a/eos/effects/subsystembonusminmatarelectronicstasiswebifierrange.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Stasis Web", - "maxRange", module.getModifiedItemAttr("subsystemBonusMinmatarElectronic"), - skill="Minmatar Electronic Systems") diff --git a/eos/effects/subsystembonusminmatarengineeringcapacitorcapacity.py b/eos/effects/subsystembonusminmatarengineeringcapacitorcapacity.py deleted file mode 100644 index 36ffe1d3f..000000000 --- a/eos/effects/subsystembonusminmatarengineeringcapacitorcapacity.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("capacitorCapacity", module.getModifiedItemAttr("subsystemBonusMinmatarCore"), - skill="Minmatar Core Systems") diff --git a/eos/effects/subsystembonusminmatarengineeringcapacitorrecharge.py b/eos/effects/subsystembonusminmatarengineeringcapacitorrecharge.py deleted file mode 100644 index 3919fbd1c..000000000 --- a/eos/effects/subsystembonusminmatarengineeringcapacitorrecharge.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("rechargeRate", module.getModifiedItemAttr("subsystemBonusMinmatarCore"), - skill="Minmatar Core Systems") diff --git a/eos/effects/subsystembonusminmatarengineeringpoweroutput.py b/eos/effects/subsystembonusminmatarengineeringpoweroutput.py deleted file mode 100644 index b034cfbc0..000000000 --- a/eos/effects/subsystembonusminmatarengineeringpoweroutput.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("powerOutput", module.getModifiedItemAttr("subsystemBonusMinmatarCore"), - skill="Minmatar Core Systems") diff --git a/eos/effects/subsystembonusminmataroffensive2projectileweaponrof.py b/eos/effects/subsystembonusminmataroffensive2projectileweaponrof.py deleted file mode 100644 index 37de5345a..000000000 --- a/eos/effects/subsystembonusminmataroffensive2projectileweaponrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Projectile Turret"), - "speed", module.getModifiedItemAttr("subsystemBonusMinmatarOffensive2"), - skill="Minmatar Offensive Systems") diff --git a/eos/effects/subsystembonusminmataroffensiveassaultmissilelauncherrof.py b/eos/effects/subsystembonusminmataroffensiveassaultmissilelauncherrof.py deleted file mode 100644 index 545dac076..000000000 --- a/eos/effects/subsystembonusminmataroffensiveassaultmissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Rapid Light", - "speed", module.getModifiedItemAttr("subsystemBonusMinmatarOffensive"), - skill="Minmatar Offensive Systems") diff --git a/eos/effects/subsystembonusminmataroffensiveheavyassaultmissilelauncherrof.py b/eos/effects/subsystembonusminmataroffensiveheavyassaultmissilelauncherrof.py deleted file mode 100644 index 0cdf31f6c..000000000 --- a/eos/effects/subsystembonusminmataroffensiveheavyassaultmissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy Assault", - "speed", module.getModifiedItemAttr("subsystemBonusMinmatarOffensive"), - skill="Minmatar Offensive Systems") diff --git a/eos/effects/subsystembonusminmataroffensiveheavymissilelauncherrof.py b/eos/effects/subsystembonusminmataroffensiveheavymissilelauncherrof.py deleted file mode 100644 index 65f3983b2..000000000 --- a/eos/effects/subsystembonusminmataroffensiveheavymissilelauncherrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy", - "speed", module.getModifiedItemAttr("subsystemBonusMinmatarOffensive"), - skill="Minmatar Offensive Systems") diff --git a/eos/effects/subsystembonusminmataroffensiveprojectileweaponrof.py b/eos/effects/subsystembonusminmataroffensiveprojectileweaponrof.py deleted file mode 100644 index 49993800a..000000000 --- a/eos/effects/subsystembonusminmataroffensiveprojectileweaponrof.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Projectile Turret"), - "speed", module.getModifiedItemAttr("subsystemBonusMinmatarOffensive"), - skill="Minmatar Offensive Systems") diff --git a/eos/effects/subsystembonusminmatarpropulsionagility.py b/eos/effects/subsystembonusminmatarpropulsionagility.py deleted file mode 100644 index 3b61c349e..000000000 --- a/eos/effects/subsystembonusminmatarpropulsionagility.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.ship.boostItemAttr("agility", module.getModifiedItemAttr("subsystemBonusMinmatarPropulsion"), - skill="Minmatar Propulsion Systems") diff --git a/eos/effects/subsystembonusoffensivejumpharmonics.py b/eos/effects/subsystembonusoffensivejumpharmonics.py deleted file mode 100644 index aab0f2334..000000000 --- a/eos/effects/subsystembonusoffensivejumpharmonics.py +++ /dev/null @@ -1,9 +0,0 @@ -# subsystemBonusOffensiveJumpHarmonics -# -# Used by: -# Subsystems named like: Offensive Covert Reconfiguration (4 of 4) -type = "passive" - - -def handler(fit, module, context): - fit.ship.forceItemAttr("jumpHarmonics", module.getModifiedItemAttr("jumpHarmonicsModifier")) diff --git a/eos/effects/subsystembonusscanprobelaunchercpu.py b/eos/effects/subsystembonusscanprobelaunchercpu.py deleted file mode 100644 index edd4a87f0..000000000 --- a/eos/effects/subsystembonusscanprobelaunchercpu.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Scan Probe Launcher", - "cpu", module.getModifiedItemAttr("cpuNeedBonus")) diff --git a/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipgroupprecursorturret.py b/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipgroupprecursorturret.py new file mode 100644 index 000000000..f1aaa88e2 --- /dev/null +++ b/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipgroupprecursorturret.py @@ -0,0 +1,10 @@ +# surgicalStrikeDamageMultiplierBonusPostPercentDamageMultiplierLocationShipGroupPrecursorTurret +# +# Used by: +# Skill: Surgical Strike +type = "passive" + + +def handler(fit, skill, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Precursor Weapon", + "damageMultiplier", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level) diff --git a/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipmodulesrequiringgunnery.py b/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipmodulesrequiringgunnery.py index 78b9750ec..481ef2604 100644 --- a/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipmodulesrequiringgunnery.py +++ b/eos/effects/surgicalstrikedamagemultiplierbonuspostpercentdamagemultiplierlocationshipmodulesrequiringgunnery.py @@ -1,7 +1,7 @@ # surgicalStrikeDamageMultiplierBonusPostPercentDamageMultiplierLocationShipModulesRequiringGunnery # # Used by: -# Implants named like: Agency 'Pyrolancea' DB Dose (3 of 3) +# Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) # Implants named like: Eifyr and Co. 'Gunslinger' Surgical Strike SS (6 of 6) # Implant: Standard Cerebral Accelerator type = "passive" diff --git a/eos/effects/systemsmallprecursorturretdamage.py b/eos/effects/systemsmallprecursorturretdamage.py new file mode 100644 index 000000000..595b9a679 --- /dev/null +++ b/eos/effects/systemsmallprecursorturretdamage.py @@ -0,0 +1,12 @@ +# systemSmallPrecursorTurretDamage +# +# Used by: +# Celestials named like: Wolf Rayet Effect Beacon Class (5 of 6) +runTime = "early" +type = ("projected", "passive") + + +def handler(fit, module, context): + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Small Precursor Weapon"), + "damageMultiplier", module.getModifiedItemAttr("smallWeaponDamageMultiplier"), + stackingPenalties=True) diff --git a/eos/effects/targetabcattack.py b/eos/effects/targetabcattack.py new file mode 100644 index 000000000..66dac5bc6 --- /dev/null +++ b/eos/effects/targetabcattack.py @@ -0,0 +1,10 @@ +# targetABCAttack +# +# Used by: +# Modules from group: Precursor Weapon (15 of 15) +type = 'active' + + +def handler(fit, module, context): + # Set reload time to 1 second + module.reloadTime = 1000 diff --git a/eos/effects/targetarmorrepair.py b/eos/effects/targetarmorrepair.py deleted file mode 100644 index 0a4c0c154..000000000 --- a/eos/effects/targetarmorrepair.py +++ /dev/null @@ -1,9 +0,0 @@ -# Not used by any item -type = "projected", "active" - - -def handler(fit, container, context): - if "projected" in context: - bonus = container.getModifiedItemAttr("armorDamageAmount") - duration = container.getModifiedItemAttr("duration") / 1000.0 - fit.extraAttributes.increase("armorRepair", bonus / duration) diff --git a/eos/effects/targetattack.py b/eos/effects/targetattack.py index 4f0625c89..c0057acc5 100644 --- a/eos/effects/targetattack.py +++ b/eos/effects/targetattack.py @@ -1,7 +1,7 @@ # targetAttack # # Used by: -# Drones from group: Combat Drone (74 of 74) +# Drones from group: Combat Drone (75 of 75) # Modules from group: Energy Weapon (212 of 214) type = 'active' diff --git a/eos/effects/targetingmaxtargetbonusmodaddmaxlockedtargetslocationchar.py b/eos/effects/targetingmaxtargetbonusmodaddmaxlockedtargetslocationchar.py index fcf70276e..d7a2004e4 100644 --- a/eos/effects/targetingmaxtargetbonusmodaddmaxlockedtargetslocationchar.py +++ b/eos/effects/targetingmaxtargetbonusmodaddmaxlockedtargetslocationchar.py @@ -2,7 +2,7 @@ # # Used by: # Skills named like: Target Management (2 of 2) -type = "passive" +type = "passive", "structure" def handler(fit, skill, context): diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py deleted file mode 100644 index 1769da1af..000000000 --- a/eos/effects/techtwocommandburstbonus.py +++ /dev/null @@ -1,8 +0,0 @@ -# Not used by any item -type = "passive" -runTime = "late" - - -def handler(fit, module, context): - for x in range(1, 4): - module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/eos/effects/thermodynamicsskilldamagebonus.py b/eos/effects/thermodynamicsskilldamagebonus.py index 54569b06a..0386ad6e6 100644 --- a/eos/effects/thermodynamicsskilldamagebonus.py +++ b/eos/effects/thermodynamicsskilldamagebonus.py @@ -6,5 +6,5 @@ type = "passive" def handler(fit, skill, context): - fit.modules.filteredItemBoost(lambda mod: True, "heatDamage", + fit.modules.filteredItemBoost(lambda mod: "heatDamage" in mod.item.attributes, "heatDamage", skill.getModifiedItemAttr("thermodynamicsHeatDamage") * skill.level) diff --git a/eos/effects/titanturretdamagescaling.py b/eos/effects/titanturretdamagescaling.py deleted file mode 100644 index a8f00c006..000000000 --- a/eos/effects/titanturretdamagescaling.py +++ /dev/null @@ -1,7 +0,0 @@ -# Not used by any item -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Gunnery"), - "turretDamageScalingRadius", ship.getModifiedItemAttr("titanBonusScalingRadius")) diff --git a/eos/effects/tractorbeamcan.py b/eos/effects/tractorbeamcan.py index 293a839ea..27517bdc0 100644 --- a/eos/effects/tractorbeamcan.py +++ b/eos/effects/tractorbeamcan.py @@ -1,6 +1,7 @@ # tractorBeamCan # # Used by: +# Deployables from group: Mobile Tractor Unit (3 of 3) # Modules from group: Tractor Beam (4 of 4) type = "active" diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index 57a58372b..7376dd563 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -4,6 +4,7 @@ # Modules from group: Missile Launcher Heavy (12 of 12) # Modules from group: Missile Launcher Rocket (15 of 15) # Modules named like: Launcher (154 of 154) +# Structure Modules named like: Standup Launcher (7 of 7) type = 'active', "projected" diff --git a/eos/effects/warpdisrupt.py b/eos/effects/warpdisrupt.py new file mode 100644 index 000000000..38b53c1fc --- /dev/null +++ b/eos/effects/warpdisrupt.py @@ -0,0 +1,10 @@ +# warpDisrupt +# +# Used by: +# Modules named like: Warp Disruptor (28 of 28) +type = "projected", "active" + + +def handler(fit, module, context): + if "projected" in context: + fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength")) diff --git a/eos/effects/warpdisruptsphere.py b/eos/effects/warpdisruptsphere.py index 22787465b..f8ce29992 100644 --- a/eos/effects/warpdisruptsphere.py +++ b/eos/effects/warpdisruptsphere.py @@ -1,3 +1,7 @@ +# warpDisruptSphere +# +# Used by: +# Modules from group: Warp Disrupt Field Generator (7 of 7) # warpDisruptSphere # diff --git a/eos/effects/warpscramble.py b/eos/effects/warpscramble.py deleted file mode 100644 index 7424625ae..000000000 --- a/eos/effects/warpscramble.py +++ /dev/null @@ -1,10 +0,0 @@ -# warpScramble -# -# Used by: -# Modules named like: Warp Disruptor (27 of 27) -type = "projected", "active" - - -def handler(fit, module, context): - if "projected" in context: - fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength")) diff --git a/eos/effects/warpscrambleblockmwdwithnpceffect.py b/eos/effects/warpscrambleblockmwdwithnpceffect.py index 89c97117e..0985d4673 100644 --- a/eos/effects/warpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/warpscrambleblockmwdwithnpceffect.py @@ -1,7 +1,7 @@ # warpScrambleBlockMWDWithNPCEffect # # Used by: -# Modules named like: Warp Scrambler (26 of 26) +# Modules named like: Warp Scrambler (27 of 27) from eos.saveddata.module import State runTime = "early" @@ -17,8 +17,8 @@ def handler(fit, module, context): # this is such a dirty hack for mod in fit.modules: 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.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: diff --git a/eos/effects/weatherbasic.py b/eos/effects/weatherbasic.py new file mode 100644 index 000000000..61b26655c --- /dev/null +++ b/eos/effects/weatherbasic.py @@ -0,0 +1,10 @@ +# weather_basic +# +# Used by: +# Celestial: basic_weather +runTime = "early" +type = ("projected", "passive") + + +def handler(fit, beacon, context): + pass diff --git a/eos/effects/weathercaustictoxin.py b/eos/effects/weathercaustictoxin.py new file mode 100644 index 000000000..c13489860 --- /dev/null +++ b/eos/effects/weathercaustictoxin.py @@ -0,0 +1,18 @@ +# weather_caustic_toxin +# +# Used by: +# Celestial: caustic_toxin_weather_1 +# Celestial: caustic_toxin_weather_2 +# Celestial: caustic_toxin_weather_3 +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/weatherdarkness.py b/eos/effects/weatherdarkness.py new file mode 100644 index 000000000..c67b7924d --- /dev/null +++ b/eos/effects/weatherdarkness.py @@ -0,0 +1,18 @@ +# weather_darkness +# +# Used by: +# Celestial: darkness_weather_1 +# Celestial: darkness_weather_2 +# Celestial: darkness_weather_3 +# Celestial: pvp_weather_1 +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 5): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/weatherelectricstorm.py b/eos/effects/weatherelectricstorm.py new file mode 100644 index 000000000..b6e78935f --- /dev/null +++ b/eos/effects/weatherelectricstorm.py @@ -0,0 +1,18 @@ +# weather_electric_storm +# +# Used by: +# Celestial: electric_storm_weather_1 +# Celestial: electric_storm_weather_2 +# Celestial: electric_storm_weather_3 +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/weatherinfernal.py b/eos/effects/weatherinfernal.py new file mode 100644 index 000000000..9ddfe1a30 --- /dev/null +++ b/eos/effects/weatherinfernal.py @@ -0,0 +1,18 @@ +# weather_infernal +# +# Used by: +# Celestial: infernal_weather_1 +# Celestial: infernal_weather_2 +# Celestial: infernal_weather_3 +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/effects/weatherxenongas.py b/eos/effects/weatherxenongas.py new file mode 100644 index 000000000..913a7de2b --- /dev/null +++ b/eos/effects/weatherxenongas.py @@ -0,0 +1,18 @@ +# weather_xenon_gas +# +# Used by: +# Celestial: xenon_gas_weather_1 +# Celestial: xenon_gas_weather_2 +# Celestial: xenon_gas_weather_3 +runTime = "early" +type = ("projected", "passive", "gang") + + +def handler(fit, beacon, context, **kwargs): + for x in range(1, 3): + if beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = beacon.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = beacon.getModifiedItemAttr("warfareBuff{}ID".format(x)) + + if id: + fit.addCommandBonus(id, value, beacon, kwargs['effect'], 'early') diff --git a/eos/gamedata.py b/eos/gamedata.py index af7d413a7..5ec2a1e14 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -166,14 +166,14 @@ class Effect(EqBase): t = t if isinstance(t, tuple) or t is None else (t,) self.__type = t - except (ImportError) as e: + except ImportError as e: # Effect probably doesn't exist, so create a dummy effect and flag it with a warning. self.__handler = effectDummy self.__runTime = "normal" self.__activeByDefault = True self.__type = None pyfalog.debug("ImportError generating handler: {0}", e) - except (AttributeError) as e: + except AttributeError as e: # Effect probably exists but there is an issue with it. Turn it into a dummy effect so we can continue, but flag it with an error. self.__handler = effectDummy self.__runTime = "normal" @@ -208,6 +208,8 @@ class Item(EqBase): MOVE_ATTR_INFO = None + ABYSSAL_TYPES = None + @classmethod def getMoveAttrInfo(cls): info = getattr(cls, "MOVE_ATTR_INFO", None) @@ -237,7 +239,7 @@ class Item(EqBase): self.__offensive = None self.__assistive = None self.__overrides = None - self.__price = None + self.__priceObj = None @property def attributes(self): @@ -372,7 +374,8 @@ class Item(EqBase): 12 : "sisters", # Amarr + Gallente 16 : "jove", 32 : "sansha", # Incrusion Sansha - 128: "ore" + 128: "ore", + 135: "triglavian" } # Race is None by default race = None @@ -443,24 +446,38 @@ class Item(EqBase): @property def price(self): - # todo: use `from sqlalchemy import inspect` instead (mac-deprecated doesn't have inspect(), was imp[lemented in 0.8) - if self.__price is not None and getattr(self.__price, '_sa_instance_state', None) and self.__price._sa_instance_state.deleted: + if self.__priceObj is not None and getattr(self.__priceObj, '_sa_instance_state', None) and self.__priceObj._sa_instance_state.deleted: pyfalog.debug("Price data for {} was deleted (probably from a cache reset), resetting object".format(self.ID)) - self.__price = None + self.__priceObj = None - if self.__price is None: + if self.__priceObj is None: db_price = eos.db.getPrice(self.ID) # do not yet have a price in the database for this item, create one if db_price is None: pyfalog.debug("Creating a price for {}".format(self.ID)) - self.__price = types_Price(self.ID) - eos.db.add(self.__price) - eos.db.commit() + self.__priceObj = types_Price(self.ID) + eos.db.add(self.__priceObj) + eos.db.flush() else: - self.__price = db_price + self.__priceObj = db_price - return self.__price + return self.__priceObj + + @property + def isAbyssal(self): + if Item.ABYSSAL_TYPES is None: + Item.getAbyssalTypes() + + return self.ID in Item.ABYSSAL_TYPES + + @classmethod + def getAbyssalTypes(cls): + cls.ABYSSAL_TYPES = eos.db.getAbyssalTypes() + + @property + def isCharge(self): + return self.category.name == "Charge" def __repr__(self): return "Item(ID={}, name={}) at {}".format( @@ -511,7 +528,15 @@ class Group(EqBase): pass -class Icon(EqBase): +class DynamicItem(EqBase): + pass + + +class DynamicItemAttribute(EqBase): + pass + + +class DynamicItemItem(EqBase): pass @@ -531,8 +556,126 @@ class MetaType(EqBase): class Unit(EqBase): - pass + def __init__(self): + self.name = None + self.displayName = None + + @property + def translations(self): + """ This is a mapping of various tweaks that we have to do between the internal representation of an attribute + value and the display (for example, 'Millisecond' units have the display name of 's', so we have to convert value + from ms to s) """ + # Each entry contains: + # Function to convert value to display value + # Function to convert value to display format (which sometimes can be a string) + # Function which controls unit name used with attribute + # Function to convert display value to value + return { + "Inverse Absolute Percent": ( + lambda v: (1 - v) * 100, + lambda v: (1 - v) * 100, + lambda u: u, + lambda d: -1 * (d / 100) + 1), + "Inversed Modifier Percent": ( + lambda v: (1 - v) * 100, + lambda v: (1 - v) * 100, + lambda u: u, + lambda d: -1 * (d / 100) + 1), + "Modifier Percent": ( + lambda v: (v - 1) * 100, + lambda v: ("%+.2f" if ((v - 1) * 100) % 1 else "%+d") % ((v - 1) * 100), + lambda u: u, + lambda d: (d / 100) + 1), + "Volume": ( + lambda v: v, + lambda v: v, + lambda u: "m³", + lambda d: d), + "Sizeclass": ( + lambda v: v, + lambda v: v, + lambda u: "", + lambda d: d), + "Absolute Percent": ( + lambda v: v * 100, + lambda v: v * 100, + lambda u: u, + lambda d: d / 100), + "Milliseconds": ( + lambda v: v / 1000, + lambda v: v / 1000, + lambda u: u, + lambda d: d * 1000), + "Boolean": ( + lambda v: True if v else False, + lambda v: "Yes" if v else "No", + lambda u: "", + lambda d: 1.0 if d == "Yes" else 0.0), + "typeID": ( + self.itemIDCallback, + self.itemIDCallback, + lambda u: "", + None), # we could probably convert these back if we really tried hard enough + "groupID": ( + self.groupIDCallback, + self.groupIDCallback, + lambda u: "", + None), + "attributeID": ( + self.attributeIDCallback, + self.attributeIDCallback, + lambda u: "", + None), + } + + @staticmethod + def itemIDCallback(v): + v = int(v) + item = eos.db.getItem(int(v)) + return "%s (%d)" % (item.name, v) if item is not None else str(v) + + @staticmethod + def groupIDCallback(v): + v = int(v) + group = eos.db.getGroup(v) + return "%s (%d)" % (group.name, v) if group is not None else str(v) + + @staticmethod + def attributeIDCallback(v): + v = int(v) + if not v: # some attributes come through with a value of 0? See #1387 + return "%d" % v + attribute = eos.db.getAttributeInfo(v, eager="unit") + return "%s (%d)" % (attribute.name.capitalize(), v) + + def PreformatValue(self, value): + """Attributes have to be translated certain ways based on their unit (ex: decimals converting to percentages). + This allows us to get an easy representation of how the attribute should be printed """ + + override = self.translations.get(self.name) + if override is not None: + return override[1](value), override[2](self.displayName) + + return value, self.displayName + + def SimplifyValue(self, value): + """Takes the internal representation value and convert it into the display value""" + + override = self.translations.get(self.name) + if override is not None: + return override[0](value) + + return value + + def ComplicateValue(self, value): + """Takes the display value and turns it back into the internal representation of it""" + + override = self.translations.get(self.name) + if override is not None: + return override[3](value) + + return value class Traits(EqBase): pass diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index 6aead3583..a8d6c4ec3 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -75,7 +75,7 @@ class FitDpsGraph(Graph): pyfalog.critical(e) for mod in fit.modules: - dps, _ = mod.damageStats(fit.targetResists) + dps = mod.getDps(targetResists=fit.targetResists).total if mod.hardpoint == Hardpoint.TURRET: if mod.state >= State.ACTIVE: total += dps * self.calculateTurretMultiplier(mod, data) @@ -88,15 +88,17 @@ class FitDpsGraph(Graph): for drone in fit.drones: multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier( drone, data) - dps, _ = drone.damageStats(fit.targetResists) + dps = drone.getDps(targetResists=fit.targetResists).total total += dps * multiplier # this is janky as fuck for fighter in fit.fighters: + if not fighter.active: + continue for ability in fighter.abilities: if ability.dealsDamage and ability.active: multiplier = self.calculateFighterMissileMultiplier(ability, data) - dps, _ = ability.damageStats(fit.targetResists) + dps = ability.getDps(targetResists=fit.targetResists).total total += dps * multiplier return total @@ -141,14 +143,14 @@ class FitDpsGraph(Graph): targetVelocity = data["velocity"] explosionRadius = ability.fighter.getModifiedItemAttr("{}ExplosionRadius".format(prefix)) explosionVelocity = ability.fighter.getModifiedItemAttr("{}ExplosionVelocity".format(prefix)) - damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix)) + damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix), None) # the following conditionals are because CCP can't keep a decent naming convention, as if fighter implementation # wasn't already fucked. if damageReductionFactor is None: damageReductionFactor = ability.fighter.getModifiedItemAttr("{}DamageReductionFactor".format(prefix)) - damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix)) + damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix), None) if damageReductionSensitivity is None: damageReductionSensitivity = ability.fighter.getModifiedItemAttr( "{}DamageReductionSensitivity".format(prefix)) diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index b4c73c958..756a9f806 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -33,6 +33,15 @@ class ItemAttrShortcut(object): return return_value or default + def getBaseAttrValue(self, key, default=0): + """ + Gets base value in this order: + Mutated value > override value > attribute value + """ + return_value = self.itemModifiedAttributes.getOriginal(key) + + return return_value or default + class ChargeAttrShortcut(object): def getModifiedChargeAttr(self, key, default=0): @@ -59,8 +68,10 @@ class ModifiedAttributeDict(collections.MutableMapping): self.__modified = {} # Affected by entities self.__affectedBy = {} - # Overrides + # Overrides (per item) self.__overrides = {} + # Mutators (per module) + self.__mutators = {} # Dictionaries for various value modification types self.__forced = {} self.__preAssigns = {} @@ -100,6 +111,14 @@ class ModifiedAttributeDict(collections.MutableMapping): def overrides(self, val): self.__overrides = val + @property + def mutators(self): + return {x.attribute.name: x for x in self.__mutators.values()} + + @mutators.setter + def mutators(self, val): + self.__mutators = val + def __getitem__(self, key): # Check if we have final calculated value key_value = self.__modified.get(key) @@ -128,14 +147,16 @@ class ModifiedAttributeDict(collections.MutableMapping): del self.__intermediary[key] def getOriginal(self, key, default=None): + val = None if self.overrides_enabled and self.overrides: - val = self.overrides.get(key, None) - else: - val = None + val = self.overrides.get(key, val) + + # mutators are overriden by overrides. x_x + val = self.mutators.get(key, val) if val is None: if self.original: - val = self.original.get(key, None) + val = self.original.get(key, val) if val is None and val != default: val = default @@ -194,6 +215,8 @@ class ModifiedAttributeDict(collections.MutableMapping): if force is not None: if cappingValue is not None: force = min(force, cappingValue) + if key in (50, 30, 48, 11): + force = round(force, 2) return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases.get(key, 0) @@ -247,7 +270,8 @@ class ModifiedAttributeDict(collections.MutableMapping): # Cap value if we have cap defined if cappingValue is not None: val = min(val, cappingValue) - + if key in (50, 30, 48, 11): + val = round(val, 2) return val def __handleSkill(self, skillName): @@ -361,7 +385,7 @@ class ModifiedAttributeDict(collections.MutableMapping): if resist: afflictPenal += "r" - self.__afflict(attributeName, "%s*" % (afflictPenal), multiplier, multiplier != 1) + self.__afflict(attributeName, "%s*" % afflictPenal, multiplier, multiplier != 1) def boost(self, attributeName, boostFactor, skill=None, *args, **kwargs): """Boost value by some percentage""" diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 655a3d3f1..4c6bf23db 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -142,14 +142,8 @@ class Booster(HandledItem, ItemAttrShortcut): copy = Booster(self.item) copy.active = self.active - # Legacy booster side effect code, disabling as not currently implemented - ''' - origSideEffects = list(self.iterSideEffects()) - copySideEffects = list(copy.iterSideEffects()) - i = 0 - while i < len(origSideEffects): - copySideEffects[i].active = origSideEffects[i].active - i += 1 - ''' + for sideEffect in self.sideEffects: + copyEffect = next(filter(lambda eff: eff.effectID == sideEffect.effectID, copy.sideEffects)) + copyEffect.active = sideEffect.active return copy diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 55edd9121..7fc112d71 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -57,8 +57,15 @@ class Character(object): def init(self): self.__skillIdMap = {} + for skill in self.__skills: self.__skillIdMap[skill.itemID] = skill + + # get a list of skills that the character does no have, and add them (removal of old skills happens in the + # Skill loading) + for skillID in set(self.getSkillIDMap().keys()).difference(set(self.__skillIdMap.keys())): + self.addSkill(Skill(self, skillID, self.defaultLevel)) + self.dirtySkills = set() self.alphaClone = None @@ -118,9 +125,16 @@ class Character(object): return all0 + def apiUpdateCharSheet(self, skills, secStatus=0.00): + self.clearSkills() + for skillRow in skills: + self.addSkill(Skill(self, skillRow["typeID"], skillRow["level"])) + self.secStatus = float(secStatus) + def clearSkills(self): del self.__skills[:] self.__skillIdMap.clear() + self.dirtySkills.clear() @property def ro(self): @@ -170,7 +184,6 @@ class Character(object): 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) diff --git a/eos/saveddata/damagePattern.py b/eos/saveddata/damagePattern.py index d7889f131..30015e715 100644 --- a/eos/saveddata/damagePattern.py +++ b/eos/saveddata/damagePattern.py @@ -42,13 +42,18 @@ class DamagePattern(object): return ehp def calculateEffectiveTank(self, fit, tankInfo): - ehps = {} - passiveShield = fit.calculateShieldRecharge() - ehps["passiveShield"] = self.effectivify(fit, passiveShield, "shield") - for type in ("shield", "armor", "hull"): - ehps["%sRepair" % type] = self.effectivify(fit, tankInfo["%sRepair" % type], type) - - return ehps + typeMap = { + "passiveShield": "shield", + "shieldRepair": "shield", + "armorRepair": "armor", + "armorRepairPreSpool": "armor", + "armorRepairFullSpool": "armor", + "hullRepair": "hull"} + ereps = {} + for field in tankInfo: + if field in typeMap: + ereps[field] = self.effectivify(fit, tankInfo[field], typeMap[field]) + return ereps def effectivify(self, fit, amount, type): type = type if type != "hull" else "" diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index da4c512f2..2fec27fc9 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -24,12 +24,13 @@ from sqlalchemy.orm import validates, reconstructor import eos.db from eos.effectHandlerHelpers import HandledItem, HandledCharge from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut +from eos.utils.stats import DmgTypes + pyfalog = Logger(__name__) class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): - DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal") MINING_ATTRIBUTES = ("miningAmount",) def __init__(self, item): @@ -65,8 +66,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def build(self): """ Build object. Assumes proper and valid item already set """ self.__charge = None - self.__dps = None - self.__volley = None + self.__baseVolley = None + self.__baseRemoteReps = None self.__miningyield = None self.__itemModifiedAttributes = ModifiedAttributeDict() self.__itemModifiedAttributes.original = self.__item.attributes @@ -120,32 +121,66 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def hasAmmo(self): return self.charge is not None - @property - def dps(self): - return self.damageStats() + def getVolley(self, targetResists=None): + if not self.dealsDamage or self.amountActive <= 0: + return DmgTypes(0, 0, 0, 0) + if self.__baseVolley is None: + dmgGetter = self.getModifiedChargeAttr if self.hasAmmo else self.getModifiedItemAttr + dmgMult = self.amountActive * (self.getModifiedItemAttr("damageMultiplier", 1)) + self.__baseVolley = DmgTypes( + em=(dmgGetter("emDamage", 0)) * dmgMult, + thermal=(dmgGetter("thermalDamage", 0)) * dmgMult, + kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult, + explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult) + volley = DmgTypes( + em=self.__baseVolley.em * (1 - getattr(targetResists, "emAmount", 0)), + thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)), + kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)), + explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0))) + return volley - def damageStats(self, targetResists=None): - if self.__dps is None: - self.__volley = 0 - self.__dps = 0 - if self.dealsDamage is True and self.amountActive > 0: - if self.hasAmmo: - attr = "missileLaunchDuration" - getter = self.getModifiedChargeAttr - else: - attr = "speed" - getter = self.getModifiedItemAttr + def getDps(self, targetResists=None): + volley = self.getVolley(targetResists=targetResists) + if not volley: + return DmgTypes(0, 0, 0, 0) + cycleAttr = "missileLaunchDuration" if self.hasAmmo else "speed" + cycleTime = self.getModifiedItemAttr(cycleAttr) + dpsFactor = 1 / (cycleTime / 1000) + dps = DmgTypes( + em=volley.em * dpsFactor, + thermal=volley.thermal * dpsFactor, + kinetic=volley.kinetic * dpsFactor, + explosive=volley.explosive * dpsFactor) + return dps - cycleTime = self.getModifiedItemAttr(attr) + def getRemoteReps(self, ignoreState=False): + if self.amountActive <= 0 and not ignoreState: + return (None, 0) + if self.__baseRemoteReps is None: + rrShield = self.getModifiedItemAttr("shieldBonus", 0) + rrArmor = self.getModifiedItemAttr("armorDamageAmount", 0) + rrHull = self.getModifiedItemAttr("structureDamageAmount", 0) + if rrShield: + rrType = "Shield" + rrAmount = rrShield + elif rrArmor: + rrType = "Armor" + rrAmount = rrArmor + elif rrHull: + rrType = "Hull" + rrAmount = rrHull + else: + rrType = None + rrAmount = 0 + if rrAmount: + droneAmount = self.amount if ignoreState else self.amountActive + rrAmount *= droneAmount / (self.cycleTime / 1000) + self.__baseRemoteReps = (rrType, rrAmount) + return self.__baseRemoteReps - volley = sum( - [(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 - self.__dps = volley / (cycleTime / 1000.0) - - return self.__dps, self.__volley + def changeType(self, typeID): + self.itemID = typeID + self.init() @property def miningStats(self): @@ -204,8 +239,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return val def clear(self): - self.__dps = None - self.__volley = None + self.__baseVolley = None + self.__baseRemoteReps = None self.__miningyield = None self.itemModifiedAttributes.clear() self.chargeModifiedAttributes.clear() diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 180838e31..4b929d594 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -26,6 +26,7 @@ from eos.effectHandlerHelpers import HandledItem, HandledCharge from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut from eos.saveddata.fighterAbility import FighterAbility from eos.saveddata.module import Slot +from eos.utils.stats import DmgTypes pyfalog = Logger(__name__) @@ -53,6 +54,20 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.build() + standardAttackActive = False + for ability in self.abilities: + if ability.effect.isImplemented and ability.effect.handlerName == 'fighterabilityattackm': + # Activate "standard attack" if available + ability.active = True + standardAttackActive = True + else: + # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active + if ability.effect.isImplemented and \ + standardAttackActive is False and \ + ability.effect.handlerName != 'fighterabilitymicrowarpdrive' and \ + ability.effect.handlerName != 'fighterabilityevasivemaneuvers': + ability.active = True + @reconstructor def init(self): """Initialize a fighter from the database and validate""" @@ -73,8 +88,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def build(self): """ Build object. Assumes proper and valid item already set """ self.__charge = None - self.__dps = None - self.__volley = None + self.__baseVolley = None self.__miningyield = None self.__itemModifiedAttributes = ModifiedAttributeDict() self.__chargeModifiedAttributes = ModifiedAttributeDict() @@ -158,43 +172,88 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def hasAmmo(self): return self.charge is not None - @property - def dps(self): - return self.damageStats() + def getVolley(self, targetResists=None): + if not self.active or self.amountActive <= 0: + return DmgTypes(0, 0, 0, 0) + if self.__baseVolley is None: + em = 0 + therm = 0 + kin = 0 + exp = 0 + for ability in self.abilities: + # Not passing resists here as we want to calculate and store base volley + abilityVolley = ability.getVolley() + em += abilityVolley.em + therm += abilityVolley.thermal + kin += abilityVolley.kinetic + exp += abilityVolley.explosive + self.__baseVolley = DmgTypes(em, therm, kin, exp) + volley = DmgTypes( + em=self.__baseVolley.em * (1 - getattr(targetResists, "emAmount", 0)), + thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)), + kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)), + explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0))) + return volley - def damageStats(self, targetResists=None): - if self.__dps is None: - self.__volley = 0 - self.__dps = 0 - if self.active and self.amountActive > 0: - for ability in self.abilities: - dps, volley = ability.damageStats(targetResists) - self.__dps += dps - self.__volley += volley - - # For forward compatability this assumes a fighter - # can have more than 2 damaging abilities and/or - # multiple that use charges. - if self.owner.factorReload: - activeTimes = [] - reloadTimes = [] - constantDps = 0 - for ability in self.abilities: - if not ability.active: - continue - if ability.numShots == 0: - dps, volley = ability.damageStats(targetResists) - constantDps += dps - continue - activeTimes.append(ability.numShots * ability.cycleTime) - reloadTimes.append(ability.reloadTime) - - if len(activeTimes) > 0: - shortestActive = sorted(activeTimes)[0] - longestReload = sorted(reloadTimes, reverse=True)[0] - self.__dps = max(constantDps, self.__dps * shortestActive / (shortestActive + longestReload)) - - return self.__dps, self.__volley + def getDps(self, targetResists=None): + if not self.active or self.amountActive <= 0: + return DmgTypes(0, 0, 0, 0) + # Analyze cooldowns when reload is factored in + if self.owner.factorReload: + activeTimes = [] + reloadTimes = [] + peakEm = 0 + peakTherm = 0 + peakKin = 0 + peakExp = 0 + steadyEm = 0 + steadyTherm = 0 + steadyKin = 0 + steadyExp = 0 + for ability in self.abilities: + abilityDps = ability.getDps(targetResists=targetResists) + # Peak dps + peakEm += abilityDps.em + peakTherm += abilityDps.thermal + peakKin += abilityDps.kinetic + peakExp += abilityDps.explosive + # Infinite use - add to steady dps + if ability.numShots == 0: + steadyEm += abilityDps.em + steadyTherm += abilityDps.thermal + steadyKin += abilityDps.kinetic + steadyExp += abilityDps.explosive + else: + activeTimes.append(ability.numShots * ability.cycleTime) + reloadTimes.append(ability.reloadTime) + steadyDps = DmgTypes(steadyEm, steadyTherm, steadyKin, steadyExp) + if len(activeTimes) > 0: + shortestActive = sorted(activeTimes)[0] + longestReload = sorted(reloadTimes, reverse=True)[0] + peakDps = DmgTypes(peakEm, peakTherm, peakKin, peakExp) + peakAdjustFactor = shortestActive / (shortestActive + longestReload) + peakDpsAdjusted = DmgTypes( + em=peakDps.em * peakAdjustFactor, + thermal=peakDps.thermal * peakAdjustFactor, + kinetic=peakDps.kinetic * peakAdjustFactor, + explosive=peakDps.explosive * peakAdjustFactor) + dps = max(steadyDps, peakDpsAdjusted, key=lambda d: d.total) + return dps + else: + return steadyDps + # Just sum all abilities when not taking reload into consideration + else: + em = 0 + therm = 0 + kin = 0 + exp = 0 + for ability in self.abilities: + abilityDps = ability.getDps(targetResists=targetResists) + em += abilityDps.em + therm += abilityDps.thermal + kin += abilityDps.kinetic + exp += abilityDps.explosive + return DmgTypes(em, therm, kin, exp) @property def maxRange(self): @@ -237,8 +296,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return val def clear(self): - self.__dps = None - self.__volley = None + self.__baseVolley = None self.__miningyield = None self.itemModifiedAttributes.clear() self.chargeModifiedAttributes.clear() @@ -291,6 +349,10 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __deepcopy__(self, memo): copy = Fighter(self.item) copy.amount = self.amount + copy.active = self.active + for ability in self.abilities: + copyAbility = next(filter(lambda a: a.effectID == ability.effectID, copy.abilities)) + copyAbility.active = ability.active return copy def fits(self, fit): diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index 8c32be23d..684ceec94 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -21,12 +21,12 @@ from logbook import Logger from sqlalchemy.orm import reconstructor +from eos.utils.stats import DmgTypes + pyfalog = Logger(__name__) class FighterAbility(object): - DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal") - DAMAGE_TYPES2 = ("EM", "Kin", "Exp", "Therm") # We aren't able to get data on the charges that can be stored with fighters. So we hardcode that data here, keyed # with the fighter squadron role @@ -118,30 +118,38 @@ class FighterAbility(object): return speed - def damageStats(self, targetResists=None): - if self.__dps is None: - self.__volley = 0 - self.__dps = 0 - if self.dealsDamage and self.active: - cycleTime = self.cycleTime + def getVolley(self, targetResists=None): + if not self.dealsDamage or not self.active: + return DmgTypes(0, 0, 0, 0) + if self.attrPrefix == "fighterAbilityLaunchBomb": + em = self.fighter.getModifiedChargeAttr("emDamage", 0) + therm = self.fighter.getModifiedChargeAttr("thermalDamage", 0) + kin = self.fighter.getModifiedChargeAttr("kineticDamage", 0) + exp = self.fighter.getModifiedChargeAttr("explosiveDamage", 0) + else: + em = self.fighter.getModifiedItemAttr("{}DamageEM".format(self.attrPrefix), 0) + therm = self.fighter.getModifiedItemAttr("{}DamageTherm".format(self.attrPrefix), 0) + kin = self.fighter.getModifiedItemAttr("{}DamageKin".format(self.attrPrefix), 0) + exp = self.fighter.getModifiedItemAttr("{}DamageExp".format(self.attrPrefix), 0) + dmgMult = self.fighter.amountActive * self.fighter.getModifiedItemAttr("{}DamageMultiplier".format(self.attrPrefix), 1) + volley = DmgTypes( + em=em * dmgMult * (1 - getattr(targetResists, "emAmount", 0)), + thermal=therm * dmgMult * (1 - getattr(targetResists, "thermalAmount", 0)), + kinetic=kin * dmgMult * (1 - getattr(targetResists, "kineticAmount", 0)), + explosive=exp * dmgMult * (1 - getattr(targetResists, "explosiveAmount", 0))) + return volley - if self.attrPrefix == "fighterAbilityLaunchBomb": - # bomb calcs - 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( - "{}Damage{}".format(self.attrPrefix, d2)) or 0) * - (1 - getattr(targetResists, "{}Amount".format(d), 0)), - self.DAMAGE_TYPES2, self.DAMAGE_TYPES)) - - volley *= self.fighter.amountActive - volley *= self.fighter.getModifiedItemAttr("{}DamageMultiplier".format(self.attrPrefix)) or 1 - self.__volley += volley - self.__dps += volley / (cycleTime / 1000.0) - - return self.__dps, self.__volley + def getDps(self, targetResists=None): + volley = self.getVolley(targetResists=targetResists) + if not volley: + return DmgTypes(0, 0, 0, 0) + dpsFactor = 1 / (self.cycleTime / 1000) + dps = DmgTypes( + em=volley.em * dpsFactor, + thermal=volley.thermal * dpsFactor, + kinetic=volley.kinetic * dpsFactor, + explosive=volley.explosive * dpsFactor) + return dps def clear(self): self.__dps = None diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index cb5bedcf9..f33a78651 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -34,8 +34,8 @@ from eos.saveddata.drone import Drone from eos.saveddata.character import Character from eos.saveddata.citadel import Citadel from eos.saveddata.module import Module, State, Slot, Hardpoint +from eos.utils.stats import DmgTypes from logbook import Logger - pyfalog = Logger(__name__) @@ -121,10 +121,11 @@ class Fit(object): def build(self): self.__extraDrains = [] self.__ehp = None - self.__weaponDPS = None + self.__weaponDpsMap = {} + self.__weaponVolleyMap = {} + self.__remoteRepMap = {} self.__minerYield = None - self.__weaponVolley = None - self.__droneDPS = None + self.__droneDps = None self.__droneVolley = None self.__droneYield = None self.__sustainableTank = None @@ -136,12 +137,6 @@ class Fit(object): self.__capUsed = None self.__capRecharge = None self.__calculatedTargets = [] - self.__remoteReps = { - "Armor" : None, - "Shield" : None, - "Hull" : None, - "Capacitor": None, - } self.factorReload = False self.boostsFits = set() self.gangBoosts = None @@ -155,9 +150,9 @@ class Fit(object): @targetResists.setter def targetResists(self, targetResists): self.__targetResists = targetResists - self.__weaponDPS = None - self.__weaponVolley = None - self.__droneDPS = None + self.__weaponDpsMap = {} + self.__weaponVolleyMap = {} + self.__droneDps = None self.__droneVolley = None @property @@ -258,11 +253,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 list(self.__projectedFits.values()) if not fit.isInvalid] + return [fit for fit in list(self.projectedFitDict.values()) if not fit.isInvalid] @property def commandFits(self): - return [fit for fit in list(self.__commandFits.values()) if not fit.isInvalid] + return [fit for fit in list(self.commandFitDict.values()) if not fit.isInvalid] def getProjectionInfo(self, fitID): return self.projectedOnto.get(fitID, None) @@ -278,41 +273,31 @@ class Fit(object): def projectedFighters(self): return self.__projectedFighters - @property - def weaponDPS(self): - if self.__weaponDPS is None: - self.calculateWeaponStats() + def getWeaponDps(self, spoolOptions=None): + if spoolOptions not in self.__weaponDpsMap: + self.calculateWeaponDmgStats(spoolOptions) + return self.__weaponDpsMap[spoolOptions] - return self.__weaponDPS + def getWeaponVolley(self, spoolOptions=None): + if spoolOptions not in self.__weaponVolleyMap: + self.calculateWeaponDmgStats(spoolOptions) + return self.__weaponVolleyMap[spoolOptions] - @property - def weaponVolley(self): - if self.__weaponVolley is None: - self.calculateWeaponStats() + def getDroneDps(self): + if self.__droneDps is None: + self.calculateDroneDmgStats() + return self.__droneDps - return self.__weaponVolley - - @property - def droneDPS(self): - if self.__droneDPS is None: - self.calculateWeaponStats() - - return self.__droneDPS - - @property - def droneVolley(self): + def getDroneVolley(self): if self.__droneVolley is None: - self.calculateWeaponStats() - + self.calculateDroneDmgStats() return self.__droneVolley - @property - def totalDPS(self): - return self.droneDPS + self.weaponDPS + def getTotalDps(self, spoolOptions=None): + return self.getDroneDps() + self.getWeaponDps(spoolOptions=spoolOptions) - @property - def totalVolley(self): - return self.droneVolley + self.weaponVolley + def getTotalVolley(self, spoolOptions=None): + return self.getDroneVolley() + self.getWeaponVolley(spoolOptions=spoolOptions) @property def minerYield(self): @@ -410,12 +395,13 @@ class Fit(object): def clear(self, projected=False, command=False): self.__effectiveTank = None - self.__weaponDPS = None + self.__weaponDpsMap = {} + self.__weaponVolleyMap = {} + self.__remoteRepMap = {} self.__minerYield = None - self.__weaponVolley = None self.__effectiveSustainableTank = None self.__sustainableTank = None - self.__droneDPS = None + self.__droneDps = None self.__droneVolley = None self.__droneYield = None self.__ehp = None @@ -427,9 +413,6 @@ class Fit(object): self.ecmProjectedStr = 1 # self.commandBonuses = {} - for remoterep_type in self.__remoteReps: - self.__remoteReps[remoterep_type] = None - del self.__calculatedTargets[:] del self.__extraDrains[:] @@ -672,6 +655,70 @@ class Fit(object): groups = ("Energy Weapon", "Hybrid Weapon") self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True) + # Localized environment effects + + if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud + self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True) + + if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_local_repair + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), + "armorDamageAmount", value, stackingPenalties=True) + + if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_remote_repair + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "armorDamageAmount", value, stackingPenalties=True) + + if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster_shield_bonus + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"), + "shieldBonus", value, stackingPenalties=True) + + if warfareBuffID == 89: # AOE_Beacon_filament_cloud_shield_booster_duration + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"), + "duration", value, stackingPenalties=True) + + # Abyssal Weather Effects + + if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty + for tankType in ("shield", "armor"): + self.ship.boostItemAttr("{}EmDamageResonance".format(tankType), value) + self.ship.boostItemAttr("emDamageResonance", value) # for hull + + if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus + self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True) + + if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty + for tankType in ("shield", "armor"): + self.ship.boostItemAttr("{}ExplosiveDamageResonance".format(tankType), value) + self.ship.boostItemAttr("explosiveDamageResonance", value) # for hull + + if warfareBuffID == 94: # Weather_xenon_gas_shield_hp_bonus + self.ship.boostItemAttr("shieldCapacity", value) # for hull + + if warfareBuffID == 95: # Weather_infernal_thermal_resistance_penalty + for tankType in ("shield", "armor"): + self.ship.boostItemAttr("{}ThermalDamageResonance".format(tankType), value) + self.ship.boostItemAttr("thermalDamageResonance", value) # for hull + + if warfareBuffID == 96: # Weather_infernal_armor_hp_bonus + self.ship.boostItemAttr("armorHP", value) # for hull + + if warfareBuffID == 97: # Weather_darkness_turret_range_penalty + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"), + "maxRange", value, stackingPenalties=True) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"), + "falloff", value, stackingPenalties=True) + + if warfareBuffID == 98: # Weather_darkness_velocity_bonus + self.ship.boostItemAttr("maxVelocity", value) + + if warfareBuffID == 99: # Weather_caustic_toxin_kinetic_resistance_penalty + for tankType in ("shield", "armor"): + self.ship.boostItemAttr("{}KineticDamageResonance".format(tankType), value) + self.ship.boostItemAttr("kineticDamageResonance", value) # for hull + + if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus + self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True) + del self.commandBonuses[warfareBuffID] def __resetDependentCalcs(self): @@ -847,6 +894,9 @@ class Fit(object): Fill this fit's module slots with enough dummy slots so that all slots are used. This is mostly for making the life of gui's easier. GUI's can call fill() and then stop caring about empty slots completely. + + todo: want to get rid of using this from the gui/commands, and instead make it a more built-in feature within + recalc. Figure out a way to keep track of any changes to slot layout and call this automatically """ if self.ship is None: return @@ -950,17 +1000,27 @@ class Fit(object): def getNumSlots(self, type): return self.ship.getModifiedItemAttr(self.slots[type]) or 0 + def getHardpointsFree(self, type): + if type == Hardpoint.NONE: + return 1 + elif type == Hardpoint.TURRET: + return self.ship.getModifiedItemAttr('turretSlotsLeft') - self.getHardpointsUsed(Hardpoint.TURRET) + elif type == Hardpoint.MISSILE: + return self.ship.getModifiedItemAttr('launcherSlotsLeft') - self.getHardpointsUsed(Hardpoint.MISSILE) + else: + raise ValueError("%d is not a valid value for Hardpoint Enum", type) + @property def calibrationUsed(self): return self.getItemAttrOnlineSum(self.modules, 'upgradeCost') @property def pgUsed(self): - return self.getItemAttrOnlineSum(self.modules, "power") + return round(self.getItemAttrOnlineSum(self.modules, "power"), 2) @property def cpuUsed(self): - return self.getItemAttrOnlineSum(self.modules, "cpu") + return round(self.getItemAttrOnlineSum(self.modules, "cpu"), 2) @property def droneBandwidthUsed(self): @@ -1075,149 +1135,6 @@ class Fit(object): return self.__capRecharge - @property - def sustainableTank(self): - if self.__sustainableTank is None: - self.calculateSustainableTank() - - return self.__sustainableTank - - def calculateSustainableTank(self, effective=True): - if self.__sustainableTank is None: - if self.capStable and not self.factorReload: - sustainable = { - "armorRepair" : self.extraAttributes["armorRepair"], - "shieldRepair": self.extraAttributes["shieldRepair"], - "hullRepair" : self.extraAttributes["hullRepair"] - } - else: - sustainable = {} - - repairers = [] - # Map a repairer type to the attribute it uses - groupAttrMap = { - "Shield Booster": "shieldBonus", - "Ancillary Shield Booster": "shieldBonus", - "Remote Shield Booster": "shieldBonus", - "Ancillary Remote Shield Booster": "shieldBonus", - - "Armor Repair Unit": "armorDamageAmount", - "Ancillary Armor Repairer": "armorDamageAmount", - "Remote Armor Repairer": "armorDamageAmount", - "Ancillary Remote Armor Repairer": "armorDamageAmount", - - "Hull Repair Unit": "structureDamageAmount", - "Remote Hull Repairer": "structureDamageAmount", - } - # Map repairer type to attribute - groupStoreMap = { - "Shield Booster": "shieldRepair", - "Remote Shield Booster": "shieldRepair", - "Ancillary Shield Booster": "shieldRepair", - "Ancillary Remote Shield Booster": "shieldRepair", - - "Armor Repair Unit": "armorRepair", - "Remote Armor Repairer": "armorRepair", - "Ancillary Armor Repairer": "armorRepair", - "Ancillary Remote Armor Repairer": "armorRepair", - - "Hull Repair Unit": "hullRepair", - "Remote Hull Repairer": "hullRepair", - } - - capUsed = self.capUsed - for attr in ("shieldRepair", "armorRepair", "hullRepair"): - sustainable[attr] = self.extraAttributes[attr] - dict = self.extraAttributes.getAfflictions(attr) - if self in dict: - for mod, _, amount, used in dict[self]: - if not used: - continue - if mod.projected is False: - usesCap = True - try: - if mod.capUse: - capUsed -= mod.capUse - else: - usesCap = False - except AttributeError: - usesCap = False - - # Normal Repairers - if usesCap and not mod.charge: - cycleTime = mod.rawCycleTime - amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) - sustainable[attr] -= amount / (cycleTime / 1000.0) - repairers.append(mod) - # Ancillary Armor reps etc - elif usesCap and mod.charge: - cycleTime = mod.rawCycleTime - amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) - if mod.charge.name == "Nanite Repair Paste": - multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 - else: - multiplier = 1 - sustainable[attr] -= amount * multiplier / (cycleTime / 1000.0) - repairers.append(mod) - # Ancillary Shield boosters etc - elif not usesCap and mod.item.group.name in ("Ancillary Shield Booster", "Ancillary Remote Shield Booster"): - cycleTime = mod.rawCycleTime - amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) - if self.factorReload and mod.charge: - reloadtime = mod.reloadTime - else: - reloadtime = 0.0 - offdutycycle = reloadtime / ((max(mod.numShots, 1) * cycleTime) + reloadtime) - sustainable[attr] -= amount * offdutycycle / (cycleTime / 1000.0) - - # Sort repairers by efficiency. We want to use the most efficient repairers first - repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr( - groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr( - "chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) - - # Loop through every module until we're above peak recharge - # Most efficient first, as we sorted earlier. - # calculate how much the repper can rep stability & add to total - totalPeakRecharge = self.capRecharge - for mod in repairers: - if capUsed > totalPeakRecharge: - break - - if self.factorReload and mod.charge: - reloadtime = mod.reloadTime - else: - reloadtime = 0.0 - - cycleTime = mod.rawCycleTime - capPerSec = mod.capUse - - if capPerSec is not None and cycleTime is not None: - # Check how much this repper can work - sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec) - amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) - # Add the sustainable amount - - if not mod.charge: - sustainable[groupStoreMap[mod.item.group.name]] += sustainability * amount / ( - cycleTime / 1000.0) - else: - if mod.charge.name == "Nanite Repair Paste": - multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 - else: - multiplier = 1 - ondutycycle = (max(mod.numShots, 1) * cycleTime) / ( - (max(mod.numShots, 1) * cycleTime) + reloadtime) - sustainable[groupStoreMap[ - mod.item.group.name]] += sustainability * amount * ondutycycle * multiplier / ( - cycleTime / 1000.0) - - capUsed += capPerSec - - sustainable["passiveShield"] = self.calculateShieldRecharge() - self.__sustainableTank = sustainable - - return self.__sustainableTank - def calculateCapRecharge(self, percent=PEAK_RECHARGE): capacity = self.ship.getModifiedItemAttr("capacitorCapacity") rechargeRate = self.ship.getModifiedItemAttr("rechargeRate") / 1000.0 @@ -1301,92 +1218,27 @@ class Fit(object): self.__capStable = True self.__capState = 100 - @property - def remoteReps(self): - force_recalc = False - for remote_type in self.__remoteReps: - if self.__remoteReps[remote_type] is None: - force_recalc = True - break + def getRemoteReps(self, spoolOptions=None): + if spoolOptions not in self.__remoteRepMap: + remoteReps = {} - if force_recalc is False: - return self.__remoteReps + for module in self.modules: + rrType, rrAmount = module.getRemoteReps(spoolOptions=spoolOptions) + if rrType: + if rrType not in remoteReps: + remoteReps[rrType] = 0 + remoteReps[rrType] += rrAmount - # We are rerunning the recalcs. Explicitly set to 0 to make sure we don't duplicate anything and correctly set - # all values to 0. - for remote_type in self.__remoteReps: - self.__remoteReps[remote_type] = 0 + for drone in self.drones: + rrType, rrAmount = drone.getRemoteReps() + if rrType: + if rrType not in remoteReps: + remoteReps[rrType] = 0 + remoteReps[rrType] += rrAmount - for stuff in chain(self.modules, self.drones): - if stuff.item: - if stuff.item.ID == 10250: - pass - remote_type = None + self.__remoteRepMap[spoolOptions] = remoteReps - # Only apply the charged multiplier if we have a charge in our ancil reppers (#1135) - if stuff.charge: - modifier = stuff.getModifiedItemAttr("chargedArmorDamageMultiplier", 1) - else: - modifier = 1 - - if isinstance(stuff, Module) and (stuff.isEmpty or stuff.state < State.ACTIVE): - continue - elif isinstance(stuff, Drone): - # drones don't have fueled charges, so simply override modifier with the amount of drones active - modifier = stuff.amountActive - - # Covert cycleTime to seconds - duration = stuff.cycleTime / 1000 - - # Skip modules with no duration. - if not duration: - continue - - remote_module_groups = { - "Remote Armor Repairer" : "Armor", - "Ancillary Remote Armor Repairer": "Armor", - "Remote Hull Repairer" : "Hull", - "Remote Shield Booster" : "Shield", - "Ancillary Remote Shield Booster": "Shield", - "Remote Capacitor Transmitter" : "Capacitor", - } - - module_group = stuff.item.group.name - - if module_group in remote_module_groups: - remote_type = remote_module_groups[module_group] - elif not isinstance(stuff, Drone): - # Module isn't in our list of remote rep modules, bail - continue - - if remote_type == "Hull": - hp = stuff.getModifiedItemAttr("structureDamageAmount", 0) - elif remote_type == "Armor": - hp = stuff.getModifiedItemAttr("armorDamageAmount", 0) - elif remote_type == "Shield": - hp = stuff.getModifiedItemAttr("shieldBonus", 0) - elif remote_type == "Capacitor": - hp = stuff.getModifiedItemAttr("powerTransferAmount", 0) - else: - droneShield = stuff.getModifiedItemAttr("shieldBonus", 0) - droneArmor = stuff.getModifiedItemAttr("armorDamageAmount", 0) - droneHull = stuff.getModifiedItemAttr("structureDamageAmount", 0) - if droneShield: - remote_type = "Shield" - hp = droneShield - elif droneArmor: - remote_type = "Armor" - hp = droneArmor - elif droneHull: - remote_type = "Hull" - hp = droneHull - else: - hp = 0 - - if hp > 0 and duration > 0: - self.__remoteReps[remote_type] += (hp * modifier) / duration - - return self.__remoteReps + return self.__remoteRepMap[spoolOptions] @property def hp(self): @@ -1409,11 +1261,14 @@ class Fit(object): @property def tank(self): - hps = {"passiveShield": self.calculateShieldRecharge()} - for type in ("shield", "armor", "hull"): - hps["%sRepair" % type] = self.extraAttributes["%sRepair" % type] - - return hps + reps = { + "passiveShield": self.calculateShieldRecharge(), + "shieldRepair": self.extraAttributes["shieldRepair"], + "armorRepair": self.extraAttributes["armorRepair"], + "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], + "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], + "hullRepair": self.extraAttributes["hullRepair"]} + return reps @property def effectiveTank(self): @@ -1421,24 +1276,153 @@ class Fit(object): if self.damagePattern is None: ehps = self.tank else: - ehps = self.damagePattern.calculateEffectiveTank(self, self.extraAttributes) + ehps = self.damagePattern.calculateEffectiveTank(self, self.tank) self.__effectiveTank = ehps return self.__effectiveTank + @property + def sustainableTank(self): + if self.__sustainableTank is None: + self.calculateSustainableTank() + + return self.__sustainableTank + @property def effectiveSustainableTank(self): if self.__effectiveSustainableTank is None: if self.damagePattern is None: - eshps = self.sustainableTank + tank = self.sustainableTank else: - eshps = self.damagePattern.calculateEffectiveTank(self, self.sustainableTank) - - self.__effectiveSustainableTank = eshps - + tank = self.damagePattern.calculateEffectiveTank(self, self.sustainableTank) + self.__effectiveSustainableTank = tank return self.__effectiveSustainableTank + def calculateSustainableTank(self): + if self.__sustainableTank is None: + sustainable = { + "passiveShield": self.calculateShieldRecharge(), + "shieldRepair": self.extraAttributes["shieldRepair"], + "armorRepair": self.extraAttributes["armorRepair"], + "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], + "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], + "hullRepair": self.extraAttributes["hullRepair"]} + if not self.capStable or self.factorReload: + # Map a local repairer type to the attribute it uses + groupAttrMap = { + "Shield Booster": "shieldBonus", + "Ancillary Shield Booster": "shieldBonus", + "Armor Repair Unit": "armorDamageAmount", + "Ancillary Armor Repairer": "armorDamageAmount", + "Hull Repair Unit": "structureDamageAmount"} + # Map local repairer type to tank type + groupStoreMap = { + "Shield Booster": "shieldRepair", + "Ancillary Shield Booster": "shieldRepair", + "Armor Repair Unit": "armorRepair", + "Ancillary Armor Repairer": "armorRepair", + "Hull Repair Unit": "hullRepair"} + repairers = [] + localAdjustment = {"shieldRepair": 0, "armorRepair": 0, "hullRepair": 0} + capUsed = self.capUsed + for tankType in localAdjustment: + dict = self.extraAttributes.getAfflictions(tankType) + if self in dict: + for mod, _, amount, used in dict[self]: + if not used: + continue + if mod.projected: + continue + if mod.item.group.name not in groupAttrMap: + continue + usesCap = True + try: + if mod.capUse: + capUsed -= mod.capUse + else: + usesCap = False + except AttributeError: + usesCap = False + + # Normal Repairers + if usesCap and not mod.charge: + cycleTime = mod.rawCycleTime + amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + localAdjustment[tankType] -= amount / (cycleTime / 1000.0) + repairers.append(mod) + # Ancillary Armor reps etc + elif usesCap and mod.charge: + cycleTime = mod.rawCycleTime + amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + if mod.charge.name == "Nanite Repair Paste": + multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 + else: + multiplier = 1 + localAdjustment[tankType] -= amount * multiplier / (cycleTime / 1000.0) + repairers.append(mod) + # Ancillary Shield boosters etc + elif not usesCap and mod.item.group.name in ("Ancillary Shield Booster", "Ancillary Remote Shield Booster"): + cycleTime = mod.rawCycleTime + amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + if self.factorReload and mod.charge: + reloadtime = mod.reloadTime + else: + reloadtime = 0.0 + offdutycycle = reloadtime / ((max(mod.numShots, 1) * cycleTime) + reloadtime) + localAdjustment[tankType] -= amount * offdutycycle / (cycleTime / 1000.0) + + # Sort repairers by efficiency. We want to use the most efficient repairers first + repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr( + groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr( + "chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) + + # Loop through every module until we're above peak recharge + # Most efficient first, as we sorted earlier. + # calculate how much the repper can rep stability & add to total + totalPeakRecharge = self.capRecharge + for mod in repairers: + if capUsed > totalPeakRecharge: + break + + if self.factorReload and mod.charge: + reloadtime = mod.reloadTime + else: + reloadtime = 0.0 + + cycleTime = mod.rawCycleTime + capPerSec = mod.capUse + + if capPerSec is not None and cycleTime is not None: + # Check how much this repper can work + sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec) + amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) + # Add the sustainable amount + if not mod.charge: + localAdjustment[groupStoreMap[mod.item.group.name]] += sustainability * amount / ( + cycleTime / 1000.0) + else: + if mod.charge.name == "Nanite Repair Paste": + multiplier = mod.getModifiedItemAttr("chargedArmorDamageMultiplier") or 1 + else: + multiplier = 1 + ondutycycle = (max(mod.numShots, 1) * cycleTime) / ( + (max(mod.numShots, 1) * cycleTime) + reloadtime) + localAdjustment[groupStoreMap[ + mod.item.group.name]] += sustainability * amount * ondutycycle * multiplier / ( + cycleTime / 1000.0) + + capUsed += capPerSec + sustainable["shieldRepair"] += localAdjustment["shieldRepair"] + sustainable["armorRepair"] += localAdjustment["armorRepair"] + sustainable["armorRepairPreSpool"] += localAdjustment["armorRepair"] + sustainable["armorRepairFullSpool"] += localAdjustment["armorRepair"] + sustainable["hullRepair"] += localAdjustment["hullRepair"] + + self.__sustainableTank = sustainable + + return self.__sustainableTank + def calculateLockTime(self, radius): scanRes = self.ship.getModifiedItemAttr("scanResolution") if scanRes is not None and scanRes > 0: @@ -1461,30 +1445,30 @@ class Fit(object): self.__minerYield = minerYield self.__droneYield = droneYield - def calculateWeaponStats(self): - weaponDPS = 0 - droneDPS = 0 - weaponVolley = 0 - droneVolley = 0 + def calculateWeaponDmgStats(self, spoolOptions): + weaponVolley = DmgTypes(0, 0, 0, 0) + weaponDps = DmgTypes(0, 0, 0, 0) for mod in self.modules: - dps, volley = mod.damageStats(self.targetResists) - weaponDPS += dps - weaponVolley += volley + weaponVolley += mod.getVolley(spoolOptions=spoolOptions, targetResists=self.targetResists) + weaponDps += mod.getDps(spoolOptions=spoolOptions, targetResists=self.targetResists) + + self.__weaponVolleyMap[spoolOptions] = weaponVolley + self.__weaponDpsMap[spoolOptions] = weaponDps + + def calculateDroneDmgStats(self): + droneVolley = DmgTypes(0, 0, 0, 0) + droneDps = DmgTypes(0, 0, 0, 0) for drone in self.drones: - dps, volley = drone.damageStats(self.targetResists) - droneDPS += dps - droneVolley += volley + droneVolley += drone.getVolley(targetResists=self.targetResists) + droneDps += drone.getDps(targetResists=self.targetResists) for fighter in self.fighters: - dps, volley = fighter.damageStats(self.targetResists) - droneDPS += dps - droneVolley += volley + droneVolley += fighter.getVolley(targetResists=self.targetResists) + droneDps += fighter.getDps(targetResists=self.targetResists) - self.__weaponDPS = weaponDPS - self.__weaponVolley = weaponVolley - self.__droneDPS = droneDPS + self.__droneDps = droneDps self.__droneVolley = droneVolley @property @@ -1504,6 +1488,7 @@ class Fit(object): copy_ship.name = "%s copy" % self.name copy_ship.damagePattern = self.damagePattern copy_ship.targetResists = self.targetResists + copy_ship.implantLocation = self.implantLocation copy_ship.notes = self.notes toCopy = ( @@ -1522,12 +1507,27 @@ class Fit(object): for i in orig: c.append(deepcopy(i)) - for fit in self.projectedFits: - copy_ship.__projectedFits[fit.ID] = fit - # this bit is required -- see GH issue # 83 + # this bit is required -- see GH issue # 83 + def forceUpdateSavedata(fit): eos.db.saveddata_session.flush() eos.db.saveddata_session.refresh(fit) + for fit in self.commandFits: + copy_ship.commandFitDict[fit.ID] = fit + forceUpdateSavedata(fit) + copyCommandInfo = fit.getCommandInfo(copy_ship.ID) + originalCommandInfo = fit.getCommandInfo(self.ID) + copyCommandInfo.active = originalCommandInfo.active + forceUpdateSavedata(fit) + + for fit in self.projectedFits: + copy_ship.projectedFitDict[fit.ID] = fit + forceUpdateSavedata(fit) + copyProjectionInfo = fit.getProjectionInfo(copy_ship.ID) + originalProjectionInfo = fit.getProjectionInfo(self.ID) + copyProjectionInfo.active = originalProjectionInfo.active + forceUpdateSavedata(fit) + return copy_ship def __repr__(self): diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 054828dae..c4c2f0b00 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -17,16 +17,20 @@ # along with eos. If not, see . # =============================================================================== -from logbook import Logger - -from sqlalchemy.orm import validates, reconstructor from math import floor +from logbook import Logger +from sqlalchemy.orm import reconstructor, validates + import eos.db -from eos.effectHandlerHelpers import HandledItem, HandledCharge +from eos.effectHandlerHelpers import HandledCharge, HandledItem from eos.enum import Enum -from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut +from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict from eos.saveddata.citadel import Citadel +from eos.saveddata.mutator import Mutator +from eos.utils.float import floatUnerr +from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions +from eos.utils.stats import DmgTypes pyfalog = Logger(__name__) @@ -62,6 +66,30 @@ class Slot(Enum): FS_HEAVY = 15 +ProjectedMap = { + State.OVERHEATED: State.ACTIVE, + State.ACTIVE: State.OFFLINE, + State.OFFLINE: State.ACTIVE, + State.ONLINE: State.ACTIVE # Just in case +} + + +# Old state : New State +LocalMap = { + State.OVERHEATED: State.ACTIVE, + State.ACTIVE: State.ONLINE, + State.OFFLINE: State.ONLINE, + State.ONLINE: State.ACTIVE +} + + +# For system effects. They should only ever be online or offline +ProjectedSystem = { + State.OFFLINE: State.ONLINE, + State.ONLINE: State.OFFLINE +} + + class Hardpoint(Enum): NONE = 0 MISSILE = 1 @@ -70,18 +98,34 @@ class Hardpoint(Enum): class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): """An instance of this class represents a module together with its charge and modified attributes""" - DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") MINING_ATTRIBUTES = ("miningAmount",) + SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object") - def __init__(self, item): + def __init__(self, item, baseItem=None, mutaplasmid=None): """Initialize a module from the program""" - self.__item = item + + self.itemID = item.ID if item is not None else None + self.baseItemID = baseItem.ID if baseItem is not None else None + self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None + + if baseItem is not None: + # we're working with a mutated module, need to get abyssal module loaded with the base attributes + # Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This + # will require a bit more research though, considering there has never been a need to "swap" out the item of a Module + # before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example) + self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID) + self.__baseItem = baseItem + self.__mutaplasmid = mutaplasmid + else: + self.__item = item + self.__baseItem = baseItem + self.__mutaplasmid = mutaplasmid if item is not None and self.isInvalid: raise ValueError("Passed item is not a Module") self.__charge = None - self.itemID = item.ID if item is not None else None + self.projected = False self.state = State.ONLINE self.build() @@ -90,7 +134,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def init(self): """Initialize a module from the database and validate""" self.__item = None + self.__baseItem = None self.__charge = None + self.__mutaplasmid = None # we need this early if module is invalid and returns early self.__slot = self.dummySlot @@ -101,6 +147,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): pyfalog.error("Item (id: {0}) does not exist", self.itemID) return + if self.baseItemID: + self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID) + self.__baseItem = eos.db.getItem(self.baseItemID) + self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID) + if self.__baseItem is None: + pyfalog.error("Base Item (id: {0}) does not exist", self.itemID) + return + if self.isInvalid: pyfalog.error("Item (id: {0}) is not a Module", self.itemID) return @@ -116,9 +170,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.__charge and self.__charge.category.name != "Charge": self.__charge = None - self.__dps = None + self.__baseVolley = None + self.__baseRemoteReps = None self.__miningyield = None - self.__volley = None self.__reloadTime = None self.__reloadForce = None self.__chargeCycles = None @@ -132,6 +186,18 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__itemModifiedAttributes.overrides = self.__item.overrides self.__hardpoint = self.__calculateHardpoint(self.__item) self.__slot = self.__calculateSlot(self.__item) + + # Instantiate / remove mutators if this is a mutated module + if self.__baseItem: + for x in self.mutaplasmid.attributes: + attr = self.item.attributes[x.name] + id = attr.ID + if id not in self.mutators: # create the mutator + Mutator(self, attr, attr.value) + # @todo: remove attributes that are no longer part of the mutaplasmid. + + self.__itemModifiedAttributes.mutators = self.mutators + if self.__charge: self.__chargeModifiedAttributes.original = self.__charge.attributes self.__chargeModifiedAttributes.overrides = self.__charge.overrides @@ -144,10 +210,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return empty @classmethod - def buildRack(cls, slot): + def buildRack(cls, slot, num=None): empty = Rack(None) empty.__slot = slot empty.dummySlot = slot + empty.num = num return empty @property @@ -160,11 +227,17 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def isInvalid(self): + # todo: validate baseItem as well if it's set. if self.isEmpty: return False return self.__item is None or \ (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and - self.__item.group.name != "Effect Beacon") + self.__item.group.name not in self.SYSTEM_GROUPS) or \ + (self.item.isAbyssal and (not self.baseItemID or not self.mutaplasmidID)) + + @property + def isMutated(self): + return self.baseItemID or self.mutaplasmidID @property def numCharges(self): @@ -176,8 +249,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if chargeVolume is None or containerCapacity is None: charges = 0 else: - charges = floor(containerCapacity / chargeVolume) - return int(charges) + charges = int(floatUnerr(containerCapacity / chargeVolume)) + return charges @property def numShots(self): @@ -201,7 +274,17 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def modPosition(self): if self.owner: - return self.owner.modules.index(self) + return self.owner.modules.index(self) if not self.isProjected else self.owner.projectedModules.index(self) + + @property + def isProjected(self): + if self.owner: + return self in self.owner.projectedModules + return None + + @property + def isExclusiveSystemEffect(self): + return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments") @property def isCapitalSize(self): @@ -304,6 +387,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def item(self): return self.__item if self.__item != 0 else None + @property + def baseItem(self): + return self.__baseItem + + @property + def mutaplasmid(self): + return self.__mutaplasmid + @property def charge(self): return self.__charge if self.__charge != 0 else None @@ -322,33 +413,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__itemModifiedAttributes.clear() - def damageStats(self, targetResists): - if self.__dps is None: - self.__dps = 0 - self.__volley = 0 - - if not self.isEmpty and self.state >= State.ACTIVE: - if self.charge: - func = self.getModifiedChargeAttr - else: - func = self.getModifiedItemAttr - - 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 - # Some weapons repeat multiple times in one cycle (think doomsdays) - # Get the number of times it fires off - weaponDoT = max( - self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), - 1 - ) - - self.__volley = volley - self.__dps = (volley * weaponDoT) / (cycleTime / 1000.0) - - return self.__dps, self.__volley - @property def miningStats(self): if self.__miningyield is None: @@ -368,13 +432,110 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return self.__miningyield - @property - def dps(self): - return self.damageStats(None)[0] + def getVolley(self, spoolOptions=None, targetResists=None, ignoreState=False): + if self.isEmpty or (self.state < State.ACTIVE and not ignoreState): + return DmgTypes(0, 0, 0, 0) + if self.__baseVolley is None: + dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr + dmgMult = self.getModifiedItemAttr("damageMultiplier", 1) + self.__baseVolley = DmgTypes( + em=(dmgGetter("emDamage", 0)) * dmgMult, + thermal=(dmgGetter("thermalDamage", 0)) * dmgMult, + kinetic=(dmgGetter("kineticDamage", 0)) * dmgMult, + explosive=(dmgGetter("explosiveDamage", 0)) * dmgMult) + spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self) + spoolBoost = calculateSpoolup( + self.getModifiedItemAttr("damageMultiplierBonusMax", 0), + self.getModifiedItemAttr("damageMultiplierBonusPerCycle", 0), + self.rawCycleTime / 1000, spoolType, spoolAmount)[0] + spoolMultiplier = 1 + spoolBoost + volley = DmgTypes( + em=self.__baseVolley.em * spoolMultiplier * (1 - getattr(targetResists, "emAmount", 0)), + thermal=self.__baseVolley.thermal * spoolMultiplier * (1 - getattr(targetResists, "thermalAmount", 0)), + kinetic=self.__baseVolley.kinetic * spoolMultiplier * (1 - getattr(targetResists, "kineticAmount", 0)), + explosive=self.__baseVolley.explosive * spoolMultiplier * (1 - getattr(targetResists, "explosiveAmount", 0))) + return volley - @property - def volley(self): - return self.damageStats(None)[1] + def getDps(self, spoolOptions=None, targetResists=None, ignoreState=False): + volley = self.getVolley(spoolOptions=spoolOptions, targetResists=targetResists, ignoreState=ignoreState) + if not volley: + return DmgTypes(0, 0, 0, 0) + # Some weapons repeat multiple times in one cycle (bosonic doomsdays). Get the number of times it fires off + volleysPerCycle = max(self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), 1) + dpsFactor = volleysPerCycle / (self.cycleTime / 1000) + dps = DmgTypes( + em=volley.em * dpsFactor, + thermal=volley.thermal * dpsFactor, + kinetic=volley.kinetic * dpsFactor, + explosive=volley.explosive * dpsFactor) + return dps + + def getRemoteReps(self, spoolOptions=None, ignoreState=False): + if self.isEmpty or (self.state < State.ACTIVE and not ignoreState): + return None, 0 + + def getBaseRemoteReps(module): + remoteModuleGroups = { + "Remote Armor Repairer": "Armor", + "Ancillary Remote Armor Repairer": "Armor", + "Mutadaptive Remote Armor Repairer": "Armor", + "Remote Hull Repairer": "Hull", + "Remote Shield Booster": "Shield", + "Ancillary Remote Shield Booster": "Shield", + "Remote Capacitor Transmitter": "Capacitor"} + rrType = remoteModuleGroups.get(module.item.group.name, None) + if not rrType: + return None, 0 + if rrType == "Hull": + rrAmount = module.getModifiedItemAttr("structureDamageAmount", 0) + elif rrType == "Armor": + rrAmount = module.getModifiedItemAttr("armorDamageAmount", 0) + elif rrType == "Shield": + rrAmount = module.getModifiedItemAttr("shieldBonus", 0) + elif rrType == "Capacitor": + rrAmount = module.getModifiedItemAttr("powerTransferAmount", 0) + else: + return None, 0 + if rrAmount: + rrAmount *= 1 / (self.cycleTime / 1000) + if module.item.group.name == "Ancillary Remote Armor Repairer" and module.charge: + rrAmount *= module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1) + + return rrType, rrAmount + + if self.__baseRemoteReps is None: + self.__baseRemoteReps = getBaseRemoteReps(self) + + rrType, rrAmount = self.__baseRemoteReps + + if rrType and rrAmount and self.item.group.name == "Mutadaptive Remote Armor Repairer": + spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self) + spoolBoost = calculateSpoolup( + self.getModifiedItemAttr("repairMultiplierBonusMax", 0), + self.getModifiedItemAttr("repairMultiplierBonusPerCycle", 0), + self.rawCycleTime / 1000, spoolType, spoolAmount)[0] + rrAmount *= (1 + spoolBoost) + + return rrType, rrAmount + + def getSpoolData(self, spoolOptions=None): + weaponMultMax = self.getModifiedItemAttr("damageMultiplierBonusMax", 0) + weaponMultPerCycle = self.getModifiedItemAttr("damageMultiplierBonusPerCycle", 0) + if weaponMultMax and weaponMultPerCycle: + spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self) + _, spoolCycles, spoolTime = calculateSpoolup( + weaponMultMax, weaponMultPerCycle, + self.rawCycleTime / 1000, spoolType, spoolAmount) + return spoolCycles, spoolTime + rrMultMax = self.getModifiedItemAttr("repairMultiplierBonusMax", 0) + rrMultPerCycle = self.getModifiedItemAttr("repairMultiplierBonusPerCycle", 0) + if rrMultMax and rrMultPerCycle: + spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self) + _, spoolCycles, spoolTime = calculateSpoolup( + rrMultMax, rrMultPerCycle, + self.rawCycleTime / 1000, spoolType, spoolAmount) + return spoolCycles, spoolTime + return 0, 0 @property def reloadTime(self): @@ -468,7 +629,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if max is not None: current = 0 # if self.owner != fit else -1 # Disabled, see #1278 for mod in fit.modules: - if mod.item and mod.item.groupID == self.item.groupID: + if (mod.item and mod.item.groupID == self.item.groupID and + self.modPosition != mod.modPosition): current += 1 if current >= max: @@ -476,12 +638,8 @@ 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') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: - return False - elif self.hardpoint == Hardpoint.MISSILE: - if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1: - return False + if fit.getHardpointsFree(self.hardpoint) < 1: + return False return True @@ -571,7 +729,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): for i in range(5): itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None) if itemChargeGroup is not None: - g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) + g = eos.db.getGroup(int(itemChargeGroup), eager="items.attributes") if g is None: continue for singleItem in g.items: @@ -611,7 +769,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): for effectName, slot in effectSlotMap.items(): if effectName in item.effects: return slot - if item.group.name == "Effect Beacon": + if item.group.name in Module.SYSTEM_GROUPS: return Slot.SYSTEM raise ValueError("Passed item does not fit in any known slot") @@ -630,9 +788,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return val def clear(self): - self.__dps = None + self.__baseVolley = None + self.__baseRemoteReps = None self.__miningyield = None - self.__volley = None self.__reloadTime = None self.__reloadForce = None self.__chargeCycles = None @@ -777,14 +935,48 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): else: return 0 + @staticmethod + def getProposedState(mod, click, proposedState=None): + # todo: instead of passing in module, make this a instanced function. + pyfalog.debug("Get proposed state for module.") + if mod.slot == Slot.SUBSYSTEM or mod.isEmpty: + return State.ONLINE + + if mod.slot == Slot.SYSTEM: + transitionMap = ProjectedSystem + else: + transitionMap = ProjectedMap if mod.projected else LocalMap + + currState = mod.state + + if proposedState is not None: + state = proposedState + elif click == "right": + state = State.OVERHEATED + elif click == "ctrl": + state = State.OFFLINE + else: + state = transitionMap[currState] + if not mod.isValidState(state): + state = -1 + + if mod.isValidState(state): + return state + else: + return currState + def __deepcopy__(self, memo): item = self.item if item is None: copy = Module.buildEmpty(self.slot) else: - copy = Module(self.item) + copy = Module(self.item, self.baseItem, self.mutaplasmid) copy.charge = self.charge copy.state = self.state + + for x in self.mutators.values(): + Mutator(copy, x.attribute, x.value) + return copy def __repr__(self): @@ -799,6 +991,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): class Rack(Module): """ This is simply the Module class named something else to differentiate - it for app logic. This class does not do anything special + it for app logic. The only thing interesting about it is the num property, + which is the number of slots for this rack """ - pass + num = None diff --git a/eos/saveddata/mutator.py b/eos/saveddata/mutator.py new file mode 100644 index 000000000..07af6876f --- /dev/null +++ b/eos/saveddata/mutator.py @@ -0,0 +1,131 @@ +# =============================================================================== +# Copyright (C) 2015 Ryan Holmes +# +# 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 . +# =============================================================================== + +from logbook import Logger + +from sqlalchemy.orm import validates, reconstructor + +import eos.db +from eos.eqBase import EqBase + +pyfalog = Logger(__name__) + + +class Mutator(EqBase): + """ Mutators are the object that represent an attribute override on the module level, in conjunction with + mutaplasmids. Each mutated module, when created, is instantiated with a list of these objects, dictated by the + mutaplasmid that is used on the base module. + + A note on the different attributes on this object: + * attribute: points to the definition of the attribute from dgmattribs. + * baseAttribute: points to the attribute defined for the base item (contains the base value with with to mutate) + * dynamicAttribute: points to the Mutaplasmid definition of the attribute, including min/max + + This could probably be cleaned up with smarter relationships, but whatever + """ + + def __init__(self, module, attr, value): + # this needs to be above module assignment, as assigning the module will add it to the list and it via + # relationship and needs this set 4correctly + self.attrID = attr.ID + + self.module = module + self.moduleID = module.ID + + self.__attr = attr + self.build() + self.value = value # must run after the build(), because the validator requires build() to run first + + @reconstructor + def init(self): + self.__attr = None + + if self.attrID: + self.__attr = eos.db.getAttributeInfo(self.attrID) + if self.__attr is None: + pyfalog.error("Attribute (id: {0}) does not exist", self.attrID) + return + + self.build() + self.value = self.value # run the validator (to ensure we catch any changed min/max values might CCP release) + + def build(self): + # try...except here to catch orphaned mutators. Pretty rare, only happens so far if hacking the database + # But put it here to remove the module link if it happens, until a better solution can be developed + try: + # dynamic attribute links to the Mutaplasmids attribute definition for this mutated definition + self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID) + # base attribute links to the base ite's attribute for this mutated definition (contains original, base value) + self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name] + except: + self.module = None + + @validates("value") + def validator(self, key, val): + """ Validates values as properly falling within the range of the modules' Mutaplasmid """ + mod = val / self.baseValue + + if self.minMod <= mod <= self.maxMod: + # sweet, all good + returnVal = val + else: + # need to fudge the numbers a bit. Go with the value closest to base + if val >= 0: + returnVal = min(self.maxValue, max(self.minValue, val)) + else: + returnVal = max(self.maxValue, min(self.minValue, val)) + + return returnVal + + @property + def isInvalid(self): + # @todo: need to test what happens: + # 1) if an attribute is removed from the EVE database + # 2) if a mutaplasmid does not have the attribute anymore + # 3) if a mutaplasmid does not exist (in eve or on the module's item) + # Can remove invalid ones in a SQLAlchemy collection class... eventually + return self.__attr is None + + @property + def highIsGood(self): + return self.attribute.highIsGood + + @property + def minMod(self): + return round(self.dynamicAttribute.min, 3) + + @property + def maxMod(self): + return round(self.dynamicAttribute.max, 3) + + @property + def baseValue(self): + return self.baseAttribute.value + + @property + def minValue(self): + return self.minMod * self.baseAttribute.value + + @property + def maxValue(self): + return self.maxMod * self.baseAttribute.value + + @property + def attribute(self): + return self.__attr diff --git a/eos/saveddata/price.py b/eos/saveddata/price.py index 6618119d0..a2a630c30 100644 --- a/eos/saveddata/price.py +++ b/eos/saveddata/price.py @@ -18,24 +18,30 @@ # along with eos. If not, see . # =============================================================================== -import time -from sqlalchemy.orm import reconstructor +import time +from enum import IntEnum, unique + from logbook import Logger + pyfalog = Logger(__name__) +@unique +class PriceStatus(IntEnum): + notFetched = 0 + success = 1 + fail = 2 + notSupported = 3 + + class Price(object): def __init__(self, typeID): self.typeID = typeID self.time = 0 self.__price = 0 - self.failed = None - - @reconstructor - def init(self): - self.__item = None + self.status = PriceStatus.notFetched @property def isValid(self): @@ -43,7 +49,10 @@ class Price(object): @property def price(self): - return self.__price or 0.0 + if self.status != PriceStatus.success: + return 0 + else: + return self.__price or 0 @price.setter def price(self, price): diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 56b3f7c0d..aecb95dc8 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -29,14 +29,16 @@ pyfalog = Logger(__name__) class Ship(ItemAttrShortcut, HandledItem): EXTRA_ATTRIBUTES = { - "armorRepair" : 0, - "hullRepair" : 0, - "shieldRepair" : 0, - "maxActiveDrones" : 0, + "armorRepair": 0, + "armorRepairPreSpool": 0, + "armorRepairFullSpool": 0, + "hullRepair": 0, + "shieldRepair": 0, + "maxActiveDrones": 0, "maxTargetsLockedFromSkills": 2, - "droneControlRange" : 20000, - "cloaked" : False, - "siege" : False + "droneControlRange": 20000, + "cloaked": False, + "siege": False # We also have speedLimit for Entosis Link, but there seems to be an # issue with naming it exactly "speedLimit" due to unknown reasons. # Regardless, we don't have to put it here anyways - it will come up @@ -131,7 +133,7 @@ class Ship(ItemAttrShortcut, HandledItem): return None items = [] - g = eos.db.getGroup("Ship Modifiers", eager=("items.icon", "items.attributes")) + g = eos.db.getGroup("Ship Modifiers", eager="items.attributes") for item in g.items: # Rely on name detection because race is not reliable if item.name.lower().startswith(self.item.name.lower()): diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py index 9ffed8d46..04b57bdc9 100644 --- a/eos/saveddata/ssocharacter.py +++ b/eos/saveddata/ssocharacter.py @@ -32,24 +32,15 @@ class SsoCharacter(object): self.accessToken = accessToken self.refreshToken = refreshToken self.accessTokenExpires = None - self.esi_client = None - @reconstructor def init(self): - 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() - } + pass + def is_token_expired(self): + if self.accessTokenExpires is None: + return True + return datetime.datetime.now() >= self.accessTokenExpires def __repr__(self): return "SsoCharacter(ID={}, name={}, client={}) at {}".format( diff --git a/eos/utils/__init__.py b/eos/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eos/utils/float.py b/eos/utils/float.py new file mode 100644 index 000000000..b7bd6cc61 --- /dev/null +++ b/eos/utils/float.py @@ -0,0 +1,26 @@ +""" +Sometimes use of floats may lead to undesirable results, e.g. + int(2.3 / 0.1) = 22. +We cannot afford to use different number representations (e.g. representations +provided by decimal or fraction modules), thus consequences are worked around by +this module. +""" + +import math +import sys + + +# As we will be rounding numbers after operations (which introduce higher error +# than base float representation error), we need to keep less significant +# numbers than for single float number w/o operations +keepDigits = int(sys.float_info.dig / 2) + + +def floatUnerr(value): + """Round possible float number error, killing some precision in process.""" + if value == 0: + return value + # Find round factor, taking into consideration that we want to keep at least + # predefined amount of significant digits + roundFactor = int(keepDigits - math.ceil(math.log10(abs(value)))) + return round(value, roundFactor) diff --git a/eos/utils/spoolSupport.py b/eos/utils/spoolSupport.py new file mode 100644 index 000000000..de265731f --- /dev/null +++ b/eos/utils/spoolSupport.py @@ -0,0 +1,70 @@ +# =============================================================================== +# 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 . +# =============================================================================== + + +from collections import namedtuple +from enum import IntEnum, unique + +from eos.utils.float import floatUnerr + + +SpoolOptions = namedtuple('SpoolOptions', ('spoolType', 'spoolAmount', 'force')) + + +@unique +class SpoolType(IntEnum): + SCALE = 0 # [0..1] + TIME = 1 # Expressed via time in seconds since spool up started + CYCLES = 2 # Expressed in amount of cycles since spool up started + + +def calculateSpoolup(modMaxValue, modStepValue, modCycleTime, spoolType, spoolAmount): + """ + Calculate damage multiplier increment based on passed parameters. Module cycle time + is specified in seconds. + + Returns spoolup value, amount of cycles to reach it and time to reach it. + """ + if not modMaxValue or not modStepValue: + return 0, 0, 0 + if spoolType == SpoolType.SCALE: + cycles = int(floatUnerr(spoolAmount * modMaxValue / modStepValue)) + return cycles * modStepValue, cycles, cycles * modCycleTime + elif spoolType == SpoolType.TIME: + cycles = min(int(floatUnerr(spoolAmount / modCycleTime)), int(floatUnerr(modMaxValue / modStepValue))) + return cycles * modStepValue, cycles, cycles * modCycleTime + elif spoolType == SpoolType.CYCLES: + cycles = min(int(spoolAmount), int(floatUnerr(modMaxValue / modStepValue))) + return cycles * modStepValue, cycles, cycles * modCycleTime + else: + return 0, 0, 0 + + +def resolveSpoolOptions(spoolOptions, module): + # Rely on passed options if they are forcing us to do so + if spoolOptions is not None and spoolOptions.force: + return spoolOptions.spoolType, spoolOptions.spoolAmount + # If we're not forced to use options and module has options set, prefer on-module values + elif module is not None and module.spoolType is not None: + return module.spoolType, module.spoolAmount + # Otherwise - rely on passed options + elif spoolOptions is not None: + return spoolOptions.spoolType, spoolOptions.spoolAmount + else: + return None, None diff --git a/eos/utils/stats.py b/eos/utils/stats.py new file mode 100644 index 000000000..cec0a2bdd --- /dev/null +++ b/eos/utils/stats.py @@ -0,0 +1,70 @@ +# =============================================================================== +# 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 . +# =============================================================================== + + +class DmgTypes: + """Container for damage data stats.""" + + def __init__(self, em, thermal, kinetic, explosive): + self.em = em + self.thermal = thermal + self.kinetic = kinetic + self.explosive = explosive + self._calcTotal() + + # Iterator is needed to support tuple-style unpacking + def __iter__(self): + yield self.em + yield self.thermal + yield self.kinetic + yield self.explosive + yield self.total + + def __eq__(self, other): + if not isinstance(other, DmgTypes): + return NotImplemented + return all(( + self.em == other.em, + self.thermal == other.thermal, + self.kinetic == other.kinetic, + self.explosive == other.explosive, + self.total == other.total)) + + def __bool__(self): + return any(( + self.em, self.thermal, self.kinetic, + self.explosive, self.total)) + + def _calcTotal(self): + self.total = self.em + self.thermal + self.kinetic + self.explosive + + def __add__(self, other): + return type(self)( + em=self.em + other.em, + thermal=self.thermal + other.thermal, + kinetic=self.kinetic + other.kinetic, + explosive=self.explosive + other.explosive) + + def __iadd__(self, other): + self.em += other.em + self.thermal += other.thermal + self.kinetic += other.kinetic + self.explosive += other.explosive + self._calcTotal() + return self diff --git a/eve.db b/eve.db index 5493b4e20..417b53d1c 100644 Binary files a/eve.db and b/eve.db differ diff --git a/gui/aboutData.py b/gui/aboutData.py index 6e8ce07bc..2d658832f 100644 --- a/gui/aboutData.py +++ b/gui/aboutData.py @@ -19,7 +19,7 @@ import config -versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) +versionString = "{0}".format(config.version) licenses = ( "pyfa is released under GNU GPLv3 - see included LICENSE file", "All EVE-Online related materials are property of CCP hf.", diff --git a/gui/attribute_gauge.py b/gui/attribute_gauge.py new file mode 100644 index 000000000..e73e0461d --- /dev/null +++ b/gui/attribute_gauge.py @@ -0,0 +1,500 @@ +import math + +import wx + +from gui.utils import anim_effects + + +# todo: clean class up. Took from pyfa gauge, has a bunch of extra shit we don't need + + +class AttributeGauge(wx.Window): + def __init__(self, parent, max_range=100, animate=True, leading_edge=True, edge_on_neutral=True, guide_lines=False, size=(-1, 30), *args, + **kargs): + + super().__init__(parent, size=size, *args, **kargs) + + self._size = size + + self.guide_lines = guide_lines + + self._border_colour = wx.BLACK + self._bar_colour = None + self._bar_gradient = None + + self.leading_edge = leading_edge + self.edge_on_neutral = edge_on_neutral + + self._border_padding = 0 + self._max_range = max_range + self._value = 0 + + self._fraction_digits = 0 + + self._timer_id = wx.NewId() + self._timer = None + + self._oldValue = 0 + + self._animate = animate + self._anim_duration = 500 + self._anim_step = 0 + self._period = 20 + self._anim_value = 0 + self._anim_direction = 0 + self.anim_effect = anim_effects.OUT_QUAD + + # transition colors used based on how full (or overfilled) the gauge is. + self.transition_colors = [ + (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% + (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% + (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% + (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% + ] + + self.goodColor = wx.Colour(96, 191, 0) + self.badColor = wx.Colour(255, 64, 0) + + self.gradient_effect = -35 + + self._percentage = 0 + self._old_percentage = 0 + self._show_remaining = False + + self.SetBackgroundColour(wx.Colour(51, 51, 51)) + + self._tooltip = wx.ToolTip("0.00/100.00") + self.SetToolTip(self._tooltip) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + + def OnEraseBackground(self, event): + pass + + def OnWindowEnter(self, event): + self._show_remaining = True + self.Refresh() + + def OnWindowLeave(self, event): + self._show_remaining = False + self.Refresh() + + def GetBorderColour(self): + return self._border_colour + + def SetBorderColour(self, colour): + self._border_colour = colour + + def GetBarColour(self): + return self._bar_colour + + def SetBarColour(self, colour): + self._bar_colour = colour + + def SetFractionDigits(self, digits): + self._fraction_digits = digits + + def GetBarGradient(self): + if self._bar_gradient is None: + return None + + return self._bar_gradient[0] + + def SetBarGradient(self, gradient=None): + if gradient is None: + self._bar_gradient = None + else: + if not isinstance(gradient, list): + self._bar_gradient = [gradient] + else: + self._bar_gradient = list(gradient) + + def GetBorderPadding(self): + return self._border_padding + + def SetBorderPadding(self, padding): + self._border_padding = padding + + def GetRange(self): + """ Returns the maximum value of the gauge. """ + return self._max_range + + def Animate(self): + if self._animate: + if not self._timer: + self._timer = wx.Timer(self, self._timer_id) + + self._anim_step = 0 + self._timer.Start(self._period) + else: + self._anim_value = self._percentage + self.Refresh() + + def SetRange(self, range, reinit=False, animate=True): + """ + Sets the range of the gauge. The gauge length is its + value as a proportion of the range. + """ + + if self._max_range == range: + return + + # we cannot have a range of zero (laws of physics, etc), so we set it + if range <= 0: + self._max_range = 0.01 + else: + self._max_range = range + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value / self._max_range) * 100 + else: + self._old_percentage = self._percentage + self._percentage = 0 + self._value = 0 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range > 0.01 else 0)) + + def GetValue(self): + return self._value + + def SetValue(self, value, animate=True): + """ Sets the current position of the gauge. """ + if self._value == value: + return + + self._old_percentage = self._percentage + self._value = value + + self._percentage = (self._value / self._max_range) * 100 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range)) + + def SetValueRange(self, value, range, reinit=False): + """ Set both value and range of the gauge. """ + range_ = float(range) + + if range_ <= 0: + self._max_range = 0.01 + else: + self._max_range = range_ + + value = float(value) + + self._value = value + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value / self._max_range) * 100 + + else: + self._old_percentage = self._percentage + self._percentage = 0 + + self.Animate() + self._tooltip.SetTip("%.2f/%.2f" % + (self._value, self._max_range if float(self._max_range) > 0.01 else 0)) + + def OnPaint(self, event): + dc = wx.AutoBufferedPaintDC(self) + rect = self.GetClientRect() + + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() + + colour = self.GetBackgroundColour() + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + dc.DrawRectangle(rect) + + value = self._percentage + + if self._timer: + if self._timer.IsRunning(): + value = self._anim_value + + if self._border_colour: + dc.SetPen(wx.Pen(self.GetBorderColour())) + dc.DrawRectangle(rect) + pad = 1 + self.GetBorderPadding() + rect.Deflate(pad, pad) + + if True: + # if we have a bar color set, then we will use this + colour = self.goodColor if value >= 0 else self.badColor + + is_even = rect.width % 2 == 0 + + # the size of half our available drawing area (since we're only working in halves) + half = (rect.width / 2) + + # calculate width of bar as a percentage of half the space + w = abs(half * (value / 100)) + w = min(w, half) # Ensure that we don't overshoot our drawing area + w = math.ceil(w) # round up to nearest pixel, this ensures that we don't lose representation for sub pixels + + # print("Percentage: {}\t\t\t\t\tValue: {}\t\t\t\t\tWidth: {}\t\t\t\t\tHalf: {}\t\t\t\t\tRect Width: {}".format( + # round(self._percentage, 3), round(value,3), w, half, rect.width)) + + # set guide_lines every 10 pixels of the main gauge (not including borders) + if self.guide_lines: + for x in range(1, 20): + dc.SetBrush(wx.Brush(wx.LIGHT_GREY)) + dc.SetPen(wx.Pen(wx.LIGHT_GREY)) + dc.DrawRectangle(x * 10, 1, 1, rect.height) + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + # If we have an even width, we can simply dedicate the middle-most pixels to both sides + # However, if there is an odd width, the middle pixel is shared between the left and right gauge + + if value >= 0: + padding = (half if is_even else math.ceil(half - 1)) + 1 + dc.DrawRectangle(padding, 1, w, rect.height) + else: + padding = half - w + 1 if is_even else math.ceil(half) - (w - 1) + dc.DrawRectangle(padding, 1, w, rect.height) + + if self.leading_edge and (self.edge_on_neutral or value != 0): + dc.SetPen(wx.Pen(wx.WHITE)) + dc.SetBrush(wx.Brush(wx.WHITE)) + + if value > 0: + dc.DrawRectangle(min(padding + w, rect.width), 1, 1, rect.height) + else: + dc.DrawRectangle(max(padding - 1, 1), 1, 1, rect.height) + + def OnTimer(self, event): + old_value = self._old_percentage + value = self._percentage + start = 0 + + # -1 = left direction, 1 = right direction + direction = 1 if old_value < value else -1 + + end = direction * (value - old_value) + + self._anim_direction = direction + step = self.anim_effect(self._anim_step, start, end, self._anim_duration) + + self._anim_step += self._period + + if self._timer_id == event.GetId(): + stop_timer = False + + if self._anim_step > self._anim_duration: + stop_timer = True + + # add new value to the animation if we haven't reached our goal + # otherwise, stop animation + if direction == 1: + if old_value + step < value: + self._anim_value = old_value + step + else: + stop_timer = True + else: + if old_value - step > value: + self._anim_value = old_value - step + else: + stop_timer = True + + if stop_timer: + self._timer.Stop() + + self.Refresh() + + +if __name__ == "__main__": + import random + + def frange(x, y, jump): + while x < y: + yield x + x += jump + + class MyPanel(wx.Panel): + def __init__(self, parent, size=(500, 500)): + wx.Panel.__init__(self, parent, size=size) + box = wx.BoxSizer(wx.VERTICAL) + + self.gauge = gauge = AttributeGauge(self, size=(204, 4)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(100) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge11 = gauge = AttributeGauge(self, size=(204, 6)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(100) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge12 = gauge = AttributeGauge(self, size=(204, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(100) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge13 = gauge = AttributeGauge(self, size=(204, 10)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(100) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.value = wx.StaticText(self, label="Text") + box.Add(self.value, 0, wx.ALL | wx.CENTER, 5) + + self.btn = wx.Button(self, label="Toggle Timer") + box.Add(self.btn, 0, wx.ALL | wx.CENTER, 5) + self.btn.Bind(wx.EVT_BUTTON, self.ToggleTimer) + + self.spinCtrl = wx.SpinCtrl(self, min=-10000, max=10000) + box.Add(self.spinCtrl, 0, wx.ALL | wx.CENTER, 5) + self.spinCtrl.Bind(wx.EVT_SPINCTRL, self.UpdateValue) + + self.m_staticline2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + box.Add(self.m_staticline2, 0, wx.EXPAND, 5) + + self.spinCtrl2 = wx.SpinCtrl(self, min=0, max=10000) + box.Add(self.spinCtrl2, 0, wx.ALL | wx.CENTER, 5) + self.spinCtrl2.Bind(wx.EVT_SPINCTRL, self.UpdateValue2) + + box.Add(wx.StaticText(self, label="Large Even Pixel Test"), 0, wx.ALL | wx.CENTER, 5) + + guide_lines = False + + self.gauge2 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(204, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(2) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge3 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(204, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(-2) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + box.Add(wx.StaticText(self, label="Large Odd Pixel Test"), 0, wx.ALL | wx.CENTER, 5) + + self.gauge4 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(205, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(2) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge5 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(205, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(-2) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + box.Add(wx.StaticText(self, label="Small Even Pixel Test"), 0, wx.ALL | wx.CENTER, 5) + + self.gauge6 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(100, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(75) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge7 = gauge = AttributeGauge(self, guide_lines=guide_lines, size=(100, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(-75) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + box.Add(wx.StaticText(self, label="Small Odd Pixel Test"), 0, wx.ALL | wx.CENTER, 5) + + self.gauge8 = gauge = AttributeGauge(self, guide_lines=guide_lines, max_range=100, size=(101, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(1) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.gauge9 = gauge = AttributeGauge(self, guide_lines=guide_lines, max_range=100, size=(101, 8)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(-1) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL | wx.CENTER, 10) + + self.SetSizer(box) + self.Layout() + + self.animTimer = wx.Timer(self, wx.NewId()) + self.Bind(wx.EVT_TIMER, self.OnTimer) + + self.animTimer.Start(1000) + + def ToggleTimer(self, evt): + if self.animTimer.IsRunning: + self.animTimer.Stop() + else: + self.animTimer.Start(1000) + + def UpdateValue(self, event): + if self.animTimer.IsRunning: + self.animTimer.Stop() + num = self.spinCtrl.GetValue() + self.gauge.SetValue(num) + self.gauge11.SetValue(num) + self.gauge12.SetValue(num) + self.gauge13.SetValue(num) + self.value.SetLabel(str(num)) + + def UpdateValue2(self, event): + num = self.spinCtrl2.GetValue() + self.gauge2.SetValue(num) + self.gauge3.SetValue(num * -1) + self.gauge4.SetValue(num) + self.gauge5.SetValue(num * -1) + self.gauge6.SetValue(num) + self.gauge7.SetValue(num * -1) + self.gauge8.SetValue(num) + self.gauge9.SetValue(num * -1) + + def OnTimer(self, evt): + num = random.randint(-100, 100) + self.gauge.SetValue(num) + self.gauge11.SetValue(num) + self.gauge12.SetValue(num) + self.gauge13.SetValue(num) + self.value.SetLabel(str(num)) + + class Frame(wx.Frame): + def __init__(self, title, size=(500, 800)): + wx.Frame.__init__(self, None, title=title, size=size) + self.statusbar = self.CreateStatusBar() + main_sizer = wx.BoxSizer(wx.VERTICAL) + panel = MyPanel(self, size=size) + main_sizer.Add(panel) + self.SetSizer(main_sizer) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Attribute Bar") + top.Show() + app.MainLoop() diff --git a/gui/bitmap_loader.py b/gui/bitmap_loader.py index 9330f1fc3..7a8d41a46 100644 --- a/gui/bitmap_loader.py +++ b/gui/bitmap_loader.py @@ -19,42 +19,44 @@ import io import os.path -import zipfile from collections import OrderedDict # noinspection PyPackageRequirements import wx +from logbook import Logger import config -from logbook import Logger logging = Logger(__name__) class BitmapLoader(object): - try: - archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') - logging.info("Using zipped image files.") - except (IOError, TypeError): - logging.info("Using local image files.") - archive = None + # try: + # archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') + # logging.info("Using zipped image files.") + # except (IOError, TypeError): + # logging.info("Using local image files.") + # archive = None + + logging.info("Using local image files.") + archive = None cached_bitmaps = OrderedDict() dont_use_cached_bitmaps = False max_cached_bitmaps = 500 + scaling_factor = None + @classmethod def getStaticBitmap(cls, name, parent, location): static = wx.StaticBitmap(parent) - static.SetBitmap(cls.getBitmap(name, location)) + static.SetBitmap(cls.getBitmap(name or 0, location)) return static @classmethod def getBitmap(cls, name, location): if cls.dont_use_cached_bitmaps: - img = cls.getImage(name, location) - if img is not None: - return img.ConvertToBitmap() + return cls.loadBitmap(name, location) path = "%s%s" % (name, location) @@ -62,11 +64,7 @@ class BitmapLoader(object): cls.cached_bitmaps.popitem(False) if path not in cls.cached_bitmaps: - img = cls.getImage(name, location) - if img is not None: - bmp = img.ConvertToBitmap() - else: - bmp = None + bmp = cls.loadBitmap(name, location) cls.cached_bitmaps[path] = bmp else: bmp = cls.cached_bitmaps[path] @@ -75,8 +73,55 @@ class BitmapLoader(object): @classmethod def getImage(cls, name, location): - filename = "{0}.png".format(name) + bmp = cls.getBitmap(name, location) + if bmp is not None: + return bmp.ConvertToImage() + else: + return None + @classmethod + def loadBitmap(cls, name, location): + if cls.scaling_factor is None: + import gui.mainFrame + cls.scaling_factor = int(gui.mainFrame.MainFrame.getInstance().GetContentScaleFactor()) + + scale = cls.scaling_factor + + filename, img = cls.loadScaledBitmap(name, location, scale) + + while img is None and scale > 0: + # can't find the correctly scaled image, fallback to smaller scales + scale -= 1 + filename, img = cls.loadScaledBitmap(name, location, scale) + + if img is None: + print(("Missing icon file: {0}/{1}".format(location, filename))) + return None + + bmp: wx.Bitmap = img.ConvertToBitmap() + if scale > 1: + bmp.SetSize((bmp.GetWidth() // scale, bmp.GetHeight() // scale)) + return bmp + + @classmethod + def loadScaledBitmap(cls, name, location, scale=0): + """Attempts to load a scaled bitmap. + + Args: + name (str): TypeID or basename of the image being requested. + location (str): Path to a location that may contain the image. + scale (int): Scale factor of the image variant to load. If ``0``, attempts to load the unscaled variant. + + Returns: + (str, wx.Image): Tuple of the filename that may have been loaded and the image at that location. The + filename will always be present, but the image may be ``None``. + """ + filename = "{0}@{1}x.png".format(name, scale) if scale > 0 else "{0}.png".format(name) + img = cls.loadImage(filename, location) + return filename, img + + @classmethod + def loadImage(cls, filename, location): if cls.archive: path = os.path.join(location, filename) if os.sep != "/" and os.sep in path: @@ -94,4 +139,4 @@ class BitmapLoader(object): if os.path.exists(path): return wx.Image(path) else: - print(("Missing icon file: {0}".format(path))) + return None diff --git a/gui/builtinAdditionPanes/boosterView.py b/gui/builtinAdditionPanes/boosterView.py index 8923f83de..11bac08ca 100644 --- a/gui/builtinAdditionPanes/boosterView.py +++ b/gui/builtinAdditionPanes/boosterView.py @@ -26,6 +26,7 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit +import gui.fitCommands as cmd class BoosterViewDrop(wx.DropTarget): @@ -134,9 +135,7 @@ class BoosterView(d.Display): event.Skip() return - trigger = sFit.addBooster(fitID, event.itemID) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + if self.mainFrame.command.Submit(cmd.GuiAddBoosterCommand(fitID, event.itemID)): self.mainFrame.additionsPane.select("Boosters") event.Skip() @@ -150,9 +149,7 @@ class BoosterView(d.Display): def removeBooster(self, booster): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.removeBooster(fitID, self.origional.index(booster)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID, self.origional.index(booster))) def click(self, event): event.Skip() @@ -161,9 +158,7 @@ class BoosterView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.toggleBooster(fitID, row) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiToggleBoosterCommand(fitID, row)) def scheduleMenu(self, event): event.Skip() diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py index ed5687cfc..5734f622d 100644 --- a/gui/builtinAdditionPanes/cargoView.py +++ b/gui/builtinAdditionPanes/cargoView.py @@ -26,6 +26,7 @@ import gui.globalEvents as GE from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit from service.market import Market +import gui.fitCommands as cmd class CargoViewDrop(wx.DropTarget): @@ -80,9 +81,7 @@ class CargoView(d.Display): if data[0] == "fitting": self.swapModule(x, y, int(data[1])) elif data[0] == "market": - sFit = Fit.getInstance() - sFit.addCargo(self.mainFrame.getActiveFit(), int(data[1]), 1) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(self.mainFrame.getActiveFit(), int(data[1]))) def startDrag(self, event): row = event.GetIndex() @@ -118,18 +117,23 @@ class CargoView(d.Display): # Gather module information to get position module = fit.modules[modIdx] - if dstRow != -1: # we're swapping with 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 module.item.isAbyssal: + dlg = wx.MessageDialog(self, + "Moving this Abyssal module to the cargo will convert it to the base module. Do you wish to proceed?", + "Confirm", wx.YES_NO | wx.ICON_QUESTION) + result = dlg.ShowModal() == wx.ID_YES - if not mstate.cmdDown: # if not copying, remove module - sFit.removeModule(self.mainFrame.getActiveFit(), module.position) + if not result: + return - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="moddel", typeID=module.item.ID)) + cargoPos = dstRow if dstRow > -1 else None + + self.mainFrame.command.Submit(cmd.GuiModuleToCargoCommand( + self.mainFrame.getActiveFit(), + module.modPosition, + cargoPos, + mstate.cmdDown + )) def fitChanged(self, event): sFit = Fit.getInstance() @@ -147,7 +151,7 @@ class CargoView(d.Display): self.original = fit.cargo if fit is not None else None self.cargo = stuff = fit.cargo if fit is not None else None if stuff is not None: - stuff.sort(key=lambda cargo: cargo.itemID) + stuff.sort(key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name)) if event.fitID != self.lastFitId: self.lastFitId = event.fitID diff --git a/gui/builtinAdditionPanes/commandView.py b/gui/builtinAdditionPanes/commandView.py index 5ee0d1872..ce73e06b8 100644 --- a/gui/builtinAdditionPanes/commandView.py +++ b/gui/builtinAdditionPanes/commandView.py @@ -30,12 +30,13 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit +import gui.fitCommands as cmd class DummyItem(object): def __init__(self, txt): self.name = txt - self.icon = None + self.iconID = None class DummyEntry(object): @@ -100,21 +101,16 @@ class CommandView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: - sFit.removeCommand(fitID, self.get(row)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, self.get(row).ID)) def handleDrag(self, type, fitID): # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = Fit.getInstance() - draggedFit = sFit.getFit(fitID) - sFit.addCommandFit(activeFit, draggedFit) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) + self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(activeFit, fitID)) def startDrag(self, event): row = event.GetIndex() @@ -190,9 +186,7 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.toggleCommandFit(fitID, item) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiToggleCommandCommand(fitID, item.ID)) def scheduleMenu(self, event): event.Skip() @@ -224,8 +218,6 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() thing = self.get(row) if thing: # thing doesn't exist if it's the dummy value - sFit.removeCommand(fitID, thing) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, thing.ID)) diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index e9b893047..2918c5aa4 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -29,6 +29,7 @@ from gui.contextMenu import ContextMenu from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit from service.market import Market +import gui.fitCommands as cmd class DroneViewDrop(wx.DropTarget): @@ -144,10 +145,12 @@ class DroneView(Display): data[1] is typeID or index of data we want to manipulate """ if data[0] == "drone": # we want to merge drones - srcRow = int(data[1]) - dstRow, _ = self.HitTest((x, y)) - if srcRow != -1 and dstRow != -1: - self._merge(srcRow, dstRow) + pass + # remove merge functionality, if people complain in the next while, can add it back + # srcRow = int(data[1]) + # dstRow, _ = self.HitTest((x, y)) + # if srcRow != -1 and dstRow != -1: + # self._merge(srcRow, dstRow) elif data[0] == "market": wx.PostEvent(self.mainFrame, ItemSelected(itemID=int(data[1]))) @@ -213,9 +216,7 @@ class DroneView(Display): event.Skip() return - trigger = sFit.addDrone(fitID, event.itemID) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + if self.mainFrame.command.Submit(cmd.GuiAddDroneCommand(fitID, event.itemID)): self.mainFrame.additionsPane.select("Drones") event.Skip() @@ -230,9 +231,7 @@ class DroneView(Display): def removeDrone(self, drone): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.removeDrone(fitID, self.original.index(drone)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, self.original.index(drone))) def click(self, event): event.Skip() @@ -241,10 +240,8 @@ class DroneView(Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() drone = self.drones[row] - sFit.toggleDrone(fitID, self.original.index(drone)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiToggleDroneCommand(fitID, self.original.index(drone))) def scheduleMenu(self, event): event.Skip() diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index 3292583fd..bf797ae15 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -30,6 +30,7 @@ from gui.contextMenu import ContextMenu from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit from service.market import Market +import gui.fitCommands as cmd class FighterViewDrop(wx.DropTarget): @@ -269,11 +270,9 @@ class FighterDisplay(d.Display): event.Skip() def addItem(self, event): - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - trigger = sFit.addFighter(fitID, event.itemID) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + if self.mainFrame.command.Submit(cmd.GuiAddFighterCommand(fitID, event.itemID)): self.mainFrame.additionsPane.select("Fighters") event.Skip() @@ -288,9 +287,7 @@ class FighterDisplay(d.Display): def removeFighter(self, fighter): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.removeFighter(fitID, self.original.index(fighter)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveFighterCommand(fitID, self.original.index(fighter))) def click(self, event): event.Skip() @@ -299,10 +296,8 @@ class FighterDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() fighter = self.fighters[row] - sFit.toggleFighter(fitID, self.original.index(fighter)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiToggleFighterCommand(fitID, self.original.index(fighter))) def scheduleMenu(self, event): event.Skip() diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index b1d7bbe66..0b8521cea 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -23,11 +23,29 @@ import gui.display as d from gui.builtinMarketBrowser.events import ITEM_SELECTED import gui.mainFrame from gui.builtinViewColumns.state import State +from gui.utils.staticHelpers import DragDropHelper from gui.contextMenu import ContextMenu import gui.globalEvents as GE from eos.saveddata.fit import ImplantLocation from service.fit import Fit from service.market import Market +import gui.fitCommands as cmd + + +class ImplantViewDrop(wx.DropTarget): + def __init__(self, dropFn, *args, **kwargs): + super(ImplantViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.TextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + dragged_data = DragDropHelper.data + data = dragged_data.split(':') + self.dropFn(x, y, data) + return t class ImplantView(wx.Panel): @@ -75,10 +93,8 @@ class ImplantView(wx.Panel): def OnRadioSelect(self, event): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER) - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + if fitID is not None: + self.mainFrame.command.Submit(cmd.GuiChangeImplantLocation(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER)) class ImplantDisplay(d.Display): @@ -100,12 +116,27 @@ class ImplantDisplay(d.Display): self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) + self.SetDropTarget(ImplantViewDrop(self.handleListDrag)) + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) + def handleListDrag(self, x, y, data): + """ + Handles dragging of items from various pyfa displays which support it + + data is list with two indices: + data[0] is hard-coded str of originating source + data[1] is typeID or index of data we want to manipulate + """ + + if data[0] == "market": + if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(self.mainFrame.getActiveFit(), int(data[1]))): + self.mainFrame.additionsPane.select("Implants") + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: @@ -155,9 +186,7 @@ class ImplantDisplay(d.Display): event.Skip() return - trigger = sFit.addImplant(fitID, event.itemID) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(fitID, event.itemID)): self.mainFrame.additionsPane.select("Implants") event.Skip() @@ -175,10 +204,7 @@ class ImplantDisplay(d.Display): def removeImplant(self, implant): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - - sFit.removeImplant(fitID, self.original.index(implant)) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, self.original.index(implant))) def click(self, event): event.Skip() @@ -192,9 +218,7 @@ class ImplantDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.toggleImplant(fitID, row) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiToggleImplantCommand(fitID, row)) def scheduleMenu(self, event): event.Skip() diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py index a5654d1a4..1b941fd83 100644 --- a/gui/builtinAdditionPanes/projectedView.py +++ b/gui/builtinAdditionPanes/projectedView.py @@ -32,6 +32,7 @@ from gui.contextMenu import ContextMenu from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit from service.market import Market +import gui.fitCommands as cmd pyfalog = Logger(__name__) @@ -39,7 +40,7 @@ pyfalog = Logger(__name__) class DummyItem(object): def __init__(self, txt): self.name = txt - self.icon = None + self.iconID = None class DummyEntry(object): @@ -100,21 +101,26 @@ class ProjectedView(d.Display): data[1] is typeID or index of data we want to manipulate """ sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(self.mainFrame.getActiveFit()) if data[0] == "projected": # if source is coming from projected, we are trying to combine drones. - self.mergeDrones(x, y, int(data[1])) + pass + # removing merge functionality - if people complain about it, can add it back as a command + # self.mergeDrones(x, y, int(data[1])) elif data[0] == "fitting": dstRow, _ = self.HitTest((x, y)) # Gather module information to get position module = fit.modules[int(data[1])] - sFit.project(fit.ID, module.item.ID) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID)) + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, module.itemID, 'item')) + # sFit.project(fit.ID, module) + # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID)) elif data[0] == "market": - sFit = Fit.getInstance() - sFit.project(fit.ID, int(data[1])) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID)) + # sFit = Fit.getInstance() + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, int(data[1]), 'item')) + # sFit.project(fit.ID, int(data[1])) + # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID)) def kbEvent(self, event): keycode = event.GetKeyCode() @@ -131,10 +137,7 @@ class ProjectedView(d.Display): if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = Fit.getInstance() - draggedFit = sFit.getFit(fitID) - sFit.project(activeFit, draggedFit) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, fitID, 'fit')) def startDrag(self, event): row = event.GetIndex() @@ -185,7 +188,7 @@ class ProjectedView(d.Display): def fitChanged(self, event): sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) - pyfalog.debug("ProjectedView::fitChanged: {}", repr(fit)) + # pyfalog.debug("ProjectedView::fitChanged: {}", repr(fit)) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -319,8 +322,6 @@ class ProjectedView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() thing = self.get(row) if thing: # thing doesn't exist if it's the dummy value - sFit.removeProjected(fitID, thing) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveProjectedCommand(fitID, thing)) diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index ed23e606a..3a70896f9 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -6,9 +6,11 @@ import gui.globalEvents as GE import wx import re from service.fit import Fit +from eos.saveddata.drone import Drone from eos.saveddata.cargo import Cargo as es_Cargo from eos.saveddata.fighter import Fighter as es_Fighter from service.settings import ContextMenuSettings +import gui.fitCommands as cmd class ChangeAmount(ContextMenu): @@ -20,7 +22,7 @@ class ChangeAmount(ContextMenu): if not self.settings.get('amount'): return False - return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter") + return srcContext in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter") def getText(self, itmContext, selection): return u"Change {0} Quantity".format(itmContext) @@ -29,7 +31,7 @@ class ChangeAmount(ContextMenu): thing = selection[0] mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() - + srcContext = fullContext[0] if isinstance(thing, es_Fit): value = thing.getProjectionInfo(fitID).amount else: @@ -42,14 +44,23 @@ class ChangeAmount(ContextMenu): return sFit = Fit.getInstance() + fit = sFit.getFit(fitID) cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip()) if isinstance(thing, es_Cargo): - sFit.addCargo(fitID, thing.item.ID, int(float(cleanInput)), replace=True) + self.mainFrame.command.Submit(cmd.GuiChangeCargoQty(fitID, fit.cargo.index(thing), int(float(cleanInput)))) + return # no need for post event here + elif isinstance(thing, Drone): + if srcContext == "droneItem": + self.mainFrame.command.Submit(cmd.GuiChangeDroneQty(fitID, fit.drones.index(thing), int(float(cleanInput)))) + else: + self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneQty(fitID, fit.projectedDrones.index(thing), int(float(cleanInput)))) elif isinstance(thing, es_Fit): - sFit.changeAmount(fitID, thing, int(float(cleanInput))) + self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitQty(fitID, thing.ID, int(float(cleanInput)))) + return elif isinstance(thing, es_Fighter): - sFit.changeActiveFighters(fitID, thing, int(float(cleanInput))) + self.mainFrame.command.Submit(cmd.GuiChangeFighterQty(fitID, fit.fighters.index(thing), int(float(cleanInput)))) + return wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/boosterSideEffects.py b/gui/builtinContextMenus/boosterSideEffects.py index 60f5e2ca5..d6dbc38c6 100644 --- a/gui/builtinContextMenus/boosterSideEffects.py +++ b/gui/builtinContextMenus/boosterSideEffects.py @@ -16,7 +16,7 @@ class BoosterSideEffect(ContextMenu): # if not self.settings.get('fighterAbilities'): # return False - if self.mainFrame.getActiveFit() is None or srcContext not in ("boosterItem"): + if self.mainFrame.getActiveFit() is None or srcContext not in "boosterItem": return False self.booster = selection[0] @@ -34,6 +34,7 @@ class BoosterSideEffect(ContextMenu): label = ability.name id = ContextMenu.nextID() self.effectIds[id] = ability + menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK) menu.Bind(wx.EVT_MENU, self.handleMode, menuItem) return menuItem @@ -49,7 +50,7 @@ class BoosterSideEffect(ContextMenu): if not effect.effect.isImplemented: continue menuItem = self.addEffect(rootMenu if msw else sub, effect) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(effect.active) return sub diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 32d6370cf..3bcadfa31 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -1,8 +1,7 @@ -from gui.contextMenu import ContextMenu +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE +from gui.contextMenu import ContextMenu # noinspection PyPackageRequirements -import wx from service.fit import Fit from service.settings import ContextMenuSettings @@ -32,13 +31,12 @@ class Cargo(ContextMenu): return "Add {0} to Cargo".format(itmContext) def activate(self, fullContext, selection, i): - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() typeID = int(selection[0].ID) - sFit.addCargo(fitID, typeID) + + self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID)) self.mainFrame.additionsPane.select("Cargo") - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) Cargo.register() diff --git a/gui/builtinContextMenus/cargoAmmo.py b/gui/builtinContextMenus/cargoAmmo.py index 12ebe552f..b92ba8e3a 100644 --- a/gui/builtinContextMenus/cargoAmmo.py +++ b/gui/builtinContextMenus/cargoAmmo.py @@ -1,9 +1,7 @@ -from gui.contextMenu import ContextMenu +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE -import wx +from gui.contextMenu import ContextMenu from service.settings import ContextMenuSettings -from service.fit import Fit class CargoAmmo(ContextMenu): @@ -28,13 +26,10 @@ class CargoAmmo(ContextMenu): return "Add {0} to Cargo (x1000)".format(itmContext) def activate(self, fullContext, selection, i): - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - typeID = int(selection[0].ID) - sFit.addCargo(fitID, typeID, 1000) + self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1000)) self.mainFrame.additionsPane.select("Cargo") - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) CargoAmmo.register() diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index 936dd0cc0..97496bb75 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -1,11 +1,11 @@ # noinspection PyPackageRequirements import wx +import gui.fitCommands as cmd +import gui.mainFrame +from gui.contextMenu import ContextMenu from service.fit import Fit from service.market import Market -import gui.mainFrame -import gui.globalEvents as GE -from gui.contextMenu import ContextMenu from service.settings import ContextMenuSettings @@ -25,7 +25,7 @@ class CommandFits(ContextMenu): if evt is not None: ids = getattr(evt, 'typeID') if not isinstance(ids, set): - ids = set([ids]) + ids = {ids} 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 @@ -97,11 +97,8 @@ class CommandFits(ContextMenu): event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - - sFit.addCommandFit(fitID, fit) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(fitID, fit.ID)) CommandFits.populateFits(None) diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py index c7dae1e03..fad83f1e8 100644 --- a/gui/builtinContextMenus/droneRemoveStack.py +++ b/gui/builtinContextMenus/droneRemoveStack.py @@ -1,8 +1,7 @@ -from gui.contextMenu import ContextMenu +import gui.fitCommands as cmd import gui.mainFrame +from gui.contextMenu import ContextMenu # noinspection PyPackageRequirements -import wx -import gui.globalEvents as GE from service.fit import Fit from service.settings import ContextMenuSettings @@ -27,9 +26,7 @@ class ItemRemove(ContextMenu): fit = sFit.getFit(fitID) idx = fit.drones.index(selection[0]) - sFit.removeDrone(fitID, idx, numDronesToRemove=fit.drones[idx].amount) - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, idx, fit.drones[idx].amount)) ItemRemove.register() diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index ec52db3f8..462bd7198 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -5,6 +5,7 @@ from service.fit import Fit # noinspection PyPackageRequirements import wx from service.settings import ContextMenuSettings +import re class DroneSplit(ContextMenu): @@ -23,14 +24,93 @@ class DroneSplit(ContextMenu): def activate(self, fullContext, selection, i): srcContext = fullContext[0] - dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) - dlg.ShowModal() - dlg.Destroy() + drone = selection[0] + dlg = DroneStackSplit(self.mainFrame, drone.amount) + + if dlg.ShowModal() == wx.ID_OK: + + if dlg.input.GetLineText(0).strip() == '': + return + + sFit = Fit.getInstance() + cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip()) + fitID = self.mainFrame.getActiveFit() + + if srcContext == "droneItem": + sFit.splitDroneStack(fitID, drone, int(float(cleanInput))) + else: + sFit.splitProjectedDroneStack(fitID, drone, int(float(cleanInput))) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + # if isinstance(thing, es_Cargo): + # self.mainFrame.command.Submit( + # cmd.GuiAddCargoCommand(fitID, thing.item.ID, int(float(cleanInput)), replace=True)) + # return # no need for post event here + # elif isinstance(thing, es_Fit): + # sFit.changeAmount(fitID, thing, int(float(cleanInput))) + # elif isinstance(thing, es_Fighter): + # sFit.changeActiveFighters(fitID, thing, int(float(cleanInput))) + # + # wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) + # + # dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) + # dlg.ShowModal() + # dlg.Destroy() DroneSplit.register() +class DroneStackSplit(wx.Dialog): + def __init__(self, parent, value): + wx.Dialog.__init__(self, parent, title="Split Drone Stack") + self.SetMinSize((346, 156)) + + bSizer1 = wx.BoxSizer(wx.VERTICAL) + + bSizer2 = wx.BoxSizer(wx.VERTICAL) + text = wx.StaticText(self, wx.ID_ANY, "New Amount:") + bSizer2.Add(text, 0) + + bSizer1.Add(bSizer2, 0, wx.ALL, 10) + + self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) + self.input.SetValue(str(value)) + self.input.SelectAll() + + bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) + + bSizer3 = wx.BoxSizer(wx.VERTICAL) + bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15) + + bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) + bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) + + self.input.SetFocus() + self.input.Bind(wx.EVT_CHAR, self.onChar) + self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) + self.SetSizer(bSizer1) + self.CenterOnParent() + self.Fit() + + def processEnter(self, evt): + self.EndModal(wx.ID_OK) + + # checks to make sure it's valid number + @staticmethod + def onChar(event): + key = event.GetKeyCode() + + acceptable_characters = "1234567890" + acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste + if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters): + event.Skip() + return + else: + return False + + class DroneSpinner(wx.Dialog): def __init__(self, parent, drone, context): wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) diff --git a/gui/builtinContextMenus/fillWithModule.py b/gui/builtinContextMenus/fillWithModule.py new file mode 100644 index 000000000..0dd8d0d91 --- /dev/null +++ b/gui/builtinContextMenus/fillWithModule.py @@ -0,0 +1,34 @@ +from gui.contextMenu import ContextMenu +import gui.mainFrame +# noinspection PyPackageRequirements +import wx +import gui.globalEvents as GE +from service.settings import ContextMenuSettings +import gui.fitCommands as cmd + + +class FillWithModule(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.settings = ContextMenuSettings.getInstance() + + def display(self, srcContext, selection): + if not self.settings.get('moduleFill'): + return False + return srcContext in ("fittingModule") + + def getText(self, itmContext, selection): + return u"Fill With {0}".format(itmContext if itmContext is not None else "Module") + + def activate(self, fullContext, selection, i): + + srcContext = fullContext[0] + fitID = self.mainFrame.getActiveFit() + + if srcContext == "fittingModule": + self.mainFrame.command.Submit(cmd.GuiFillWithModuleCommand(fitID, selection[0].itemID)) + return # the command takes care of the PostEvent + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + +FillWithModule.register() diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index 8e9ede5c6..e5d083471 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -5,6 +5,7 @@ import wx import gui.globalEvents as GE from service.fit import Fit from service.settings import ContextMenuSettings +import gui.fitCommands as cmd class ItemRemove(ContextMenu): @@ -35,26 +36,33 @@ class ItemRemove(ContextMenu): fit = sFit.getFit(fitID) if srcContext == "fittingModule": - for module in selection: - if module is not None: - sFit.removeModule(fitID, fit.modules.index(module)) + modules = [module for module in selection if module is not None] + self.mainFrame.command.Submit(cmd.GuiModuleRemoveCommand(fitID, modules)) + return # the command takes care of the PostEvent elif srcContext in ("fittingCharge", "projectedCharge"): - sFit.setAmmo(fitID, None, selection) + self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, None, selection)) + return elif srcContext == "droneItem": - sFit.removeDrone(fitID, fit.drones.index(selection[0])) + self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, fit.drones.index(selection[0]))) + return elif srcContext == "fighterItem": - sFit.removeFighter(fitID, fit.fighters.index(selection[0])) + self.mainFrame.command.Submit(cmd.GuiRemoveFighterCommand(fitID, fit.fighters.index(selection[0]))) + return # the command takes care of the PostEvent elif srcContext == "implantItem": - sFit.removeImplant(fitID, fit.implants.index(selection[0])) + self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, fit.implants.index(selection[0]))) + return # the command takes care of the PostEvent elif srcContext == "boosterItem": - sFit.removeBooster(fitID, fit.boosters.index(selection[0])) + self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID, fit.boosters.index(selection[0]))) + return # the command takes care of the PostEvent elif srcContext == "cargoItem": - sFit.removeCargo(fitID, fit.cargo.index(selection[0])) + self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID, selection[0].itemID)) + return # the command takes care of the PostEvent elif srcContext in ("projectedFit", "projectedModule", "projectedDrone", "projectedFighter"): - sFit.removeProjected(fitID, selection[0]) + self.mainFrame.command.Submit(cmd.GuiRemoveProjectedCommand(fitID, selection[0])) + return # the command takes care of the PostEvent elif srcContext == "commandFit": - sFit.removeCommand(fitID, selection[0]) - + self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, selection[0].ID)) + return # the command takes care of the PostEvent wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 6221fa1e0..46b87226f 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -63,7 +63,7 @@ class ItemStats(ContextMenu): size = wx.DefaultSize pos = wx.DefaultPosition ItemStatsDialog(stuff, fullContext, pos, size, maximized) - lastWnd.closeEvent(None) + lastWnd.Close() else: ItemStatsDialog(stuff, fullContext) diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index c630312c1..a5d5e8eeb 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -26,7 +26,10 @@ class MarketJump(ContextMenu): sMkt = Market.getInstance() item = getattr(selection[0], "item", selection[0]) + isMutated = getattr(selection[0], "isMutated", False) mktGrp = sMkt.getMarketGroupByItem(item) + if mktGrp is None and isMutated: + mktGrp = sMkt.getMarketGroupByItem(selection[0].baseItem) # 1663 is Special Edition Festival Assets, we don't have root group for it if mktGrp is None or mktGrp.ID == 1663: @@ -43,7 +46,10 @@ class MarketJump(ContextMenu): if srcContext in ("fittingCharge", "projectedCharge"): item = selection[0].charge elif hasattr(selection[0], "item"): - item = selection[0].item + if getattr(selection[0], "isMutated", False): + item = selection[0].baseItem + else: + item = selection[0].item else: item = selection[0] diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index a5f6371e3..2cbbb2221 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -3,18 +3,11 @@ # noinspection PyPackageRequirements import wx -from service.fit import Fit -from service.market import Market +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE from gui.contextMenu import ContextMenu +from service.market import Market from service.settings import ContextMenuSettings -from eos.saveddata.booster import Booster -from eos.saveddata.module import Module -from eos.saveddata.drone import Drone -from eos.saveddata.fighter import Fighter -from eos.saveddata.implant import Implant -from eos.saveddata.cargo import Cargo class MetaSwap(ContextMenu): @@ -91,7 +84,7 @@ class MetaSwap(ContextMenu): # Sort items by metalevel, and group within that metalevel items = list(self.variations) - print(context) + if "implantItem" in context: # sort implants based on name items.sort(key=lambda x: x.name) @@ -122,79 +115,35 @@ class MetaSwap(ContextMenu): id = ContextMenu.nextID() mitem = wx.MenuItem(rootMenu, id, item.name) bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem) - self.moduleLookup[id] = item + + self.moduleLookup[id] = item, context m.Append(mitem) return m def handleModule(self, event): - item = self.moduleLookup.get(event.Id, None) + item, context = self.moduleLookup.get(event.Id, None) if item is None: event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - for selected_item in self.selection: - if isinstance(selected_item, Module): - pos = fit.modules.index(selected_item) - sFit.changeModule(fitID, pos, item.ID) + self.mainFrame.command.Submit(cmd.GuiMetaSwapCommand(fitID, context, item.ID, self.selection)) - elif isinstance(selected_item, Drone): - drone_count = None + # for selected_item in self.selection: - for idx, drone_stack in enumerate(fit.drones): - if drone_stack is selected_item: - drone_count = drone_stack.amount - sFit.removeDrone(fitID, idx, drone_count, False) - break - - if drone_count: - sFit.addDrone(fitID, item.ID, drone_count, True) - - elif isinstance(selected_item, Fighter): - fighter_count = None - - for idx, fighter_stack in enumerate(fit.fighters): - # Right now fighters always will have max stack size. - # Including this for future improvement, so if adjustable - # fighter stacks get added we're ready for it. - if fighter_stack is selected_item: - if fighter_stack.amount > 0: - fighter_count = fighter_stack.amount - elif fighter_stack.amount == -1: - fighter_count = fighter_stack.amountActive - else: - fighter_count.amount = 0 - - sFit.removeFighter(fitID, idx, False) - break - - sFit.addFighter(fitID, item.ID, True) - - elif isinstance(selected_item, Booster): - for idx, booster_stack in enumerate(fit.boosters): - if booster_stack is selected_item: - sFit.removeBooster(fitID, idx, False) - sFit.addBooster(fitID, item.ID, True) - break - - elif isinstance(selected_item, Implant): - for idx, implant_stack in enumerate(fit.implants): - if implant_stack is selected_item: - sFit.removeImplant(fitID, idx, False) - sFit.addImplant(fitID, item.ID, True) - break - - elif isinstance(selected_item, Cargo): - for idx, cargo_stack in enumerate(fit.cargo): - if cargo_stack is selected_item: - sFit.removeCargo(fitID, idx) - sFit.addCargo(fitID, item.ID, cargo_stack.amount, True) - break - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + # + # elif isinstance(selected_item, Drone): + # drone_count = None + # + # for idx, drone_stack in enumerate(fit.drones): + # if drone_stack is selected_item: + # drone_count = drone_stack.amount + # sFit.removeDrone(fitID, idx, drone_count, False) + # break + # + # if drone_count: + # sFit.addDrone(fitID, item.ID, drone_count, True) MetaSwap.register() diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 47a6fc87d..f4de653b4 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -3,13 +3,12 @@ # noinspection PyPackageRequirements import wx -from service.fit import Fit -from service.market import Market -from eos.saveddata.module import Hardpoint +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE -from gui.contextMenu import ContextMenu +from eos.saveddata.module import Hardpoint from gui.bitmap_loader import BitmapLoader +from gui.contextMenu import ContextMenu +from service.market import Market from service.settings import ContextMenuSettings @@ -58,9 +57,9 @@ class ModuleAmmoPicker(ContextMenu): def turretSorter(self, charge): damage = 0 - range_ = (self.module.getModifiedItemAttr("maxRange")) * \ + range_ = (self.module.item.getAttribute("maxRange")) * \ (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff")) * \ + falloff = (self.module.item.getAttribute("falloff") or 0) * \ (charge.getAttribute("fallofMultiplier") or 1) for type_ in self.DAMAGE_TYPES: d = charge.getAttribute("%sDamage" % type_) @@ -117,8 +116,8 @@ class ModuleAmmoPicker(ContextMenu): item = wx.MenuItem(menu, id_, name) menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item) item.charge = charge - if charge is not None and charge.icon is not None: - bitmap = BitmapLoader.getBitmap(charge.icon.iconFile, "icons") + if charge is not None and charge.iconID is not None: + bitmap = BitmapLoader.getBitmap(charge.iconID, "icons") if bitmap is not None: item.SetBitmap(bitmap) @@ -195,7 +194,7 @@ class ModuleAmmoPicker(ContextMenu): type_ = currType item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize()) - bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui") + bitmap = BitmapLoader.getBitmap("%s_small" % type_, "gui") if bitmap is not None: item.SetBitmap(bitmap) @@ -228,11 +227,8 @@ class ModuleAmmoPicker(ContextMenu): event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - - sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, charge.ID if charge is not None else None, self.modules)) ModuleAmmoPicker.register() diff --git a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py index 5e516e68f..8c7748975 100644 --- a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py +++ b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- +import gui.fitCommands as cmd import gui.mainFrame -# noinspection PyPackageRequirements -import wx -import gui.globalEvents as GE -from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker from eos.db.saveddata.queries import getFit as db_getFit -from service.fit import Fit +# noinspection PyPackageRequirements +from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker from service.settings import ContextMenuSettings @@ -28,20 +26,19 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker): event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = db_getFit(fitID) selectedModule = self.modules[0] + source = fit.modules if not selectedModule.isProjected else fit.projectedModules allModules = [] - for mod in fit.modules: + for mod in source: if mod.itemID is None: continue if mod.itemID == selectedModule.itemID: allModules.append(mod) - sFit.setAmmo(fitID, charge.ID if charge is not None else None, allModules) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, charge.ID if charge is not None else None, allModules)) def display(self, srcContext, selection): if not self.settings.get('moduleGlobalAmmoPicker'): diff --git a/gui/builtinContextMenus/mutaplasmids.py b/gui/builtinContextMenus/mutaplasmids.py new file mode 100644 index 000000000..98656b840 --- /dev/null +++ b/gui/builtinContextMenus/mutaplasmids.py @@ -0,0 +1,78 @@ +from gui.contextMenu import ContextMenu +import gui.mainFrame +# noinspection PyPackageRequirements +import wx +import gui.globalEvents as GE +from service.fit import Fit +from service.settings import ContextMenuSettings + + +class MutaplasmidCM(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.settings = ContextMenuSettings.getInstance() + self.eventIDs = {} + + def display(self, srcContext, selection): + + # if not self.settings.get('ammoPattern'): + # return False + + if srcContext not in "fittingModule" or self.mainFrame.getActiveFit() is None: + return False + + mod = selection[0] + if len(mod.item.mutaplasmids) == 0 and not mod.isMutated: + return False + + return True + + def getText(self, itmContext, selection): + mod = selection[0] + return "Apply Mutaplasmid" if not mod.isMutated else "Revert to {}".format(mod.baseItem.name) + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + if selection[0].isMutated: + return None + + msw = True if "wxMSW" in wx.PlatformInfo else False + self.skillIds = {} + sub = wx.Menu() + + mod = selection[0] + + menu = rootMenu if msw else sub + + for item in mod.item.mutaplasmids: + label = item.item.name + id = ContextMenu.nextID() + self.eventIDs[id] = (item, mod) + skillItem = wx.MenuItem(menu, id, label) + menu.Bind(wx.EVT_MENU, self.handleMenu, skillItem) + sub.Append(skillItem) + + return sub + + def handleMenu(self, event): + mutaplasmid, mod = self.eventIDs[event.Id] + fit = self.mainFrame.getActiveFit() + sFit = Fit.getInstance() + + # todo: dev out function to switch module to an abyssal module. Also, maybe open item stats here automatically + # with the attribute tab set? + sFit.convertMutaplasmid(fit, mod.modPosition, mutaplasmid) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) + + def activate(self, fullContext, selection, i): + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + + mod = selection[0] + sFit.changeModule(fitID, mod.modPosition, mod.baseItemID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + def getBitmap(self, context, selection): + return None + + +MutaplasmidCM.register() diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py index f06c29152..6bbb974b3 100644 --- a/gui/builtinContextMenus/project.py +++ b/gui/builtinContextMenus/project.py @@ -1,8 +1,7 @@ -from gui.contextMenu import ContextMenu +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE +from gui.contextMenu import ContextMenu # noinspection PyPackageRequirements -import wx from service.fit import Fit from service.settings import ContextMenuSettings @@ -33,12 +32,13 @@ class Project(ContextMenu): return "Project {0} onto Fit".format(itmContext) def activate(self, fullContext, selection, i): - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - trigger = sFit.project(fitID, selection[0]) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - self.mainFrame.additionsPane.select("Projected") + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, selection[0].ID, 'item')) + + # trigger = sFit.project(fitID, selection[0]) + # if trigger: + # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + # self.mainFrame.additionsPane.select("Projected") Project.register() diff --git a/gui/builtinContextMenus/tabbedFits.py b/gui/builtinContextMenus/tabbedFits.py index c55a90b35..f86562fa6 100644 --- a/gui/builtinContextMenus/tabbedFits.py +++ b/gui/builtinContextMenus/tabbedFits.py @@ -3,11 +3,11 @@ # noinspection PyPackageRequirements import wx -from service.fit import Fit +import gui.fitCommands as cmd import gui.mainFrame -import gui.globalEvents as GE -from gui.contextMenu import ContextMenu from gui.builtinViews.emptyView import BlankPage +from gui.contextMenu import ContextMenu +from service.fit import Fit class TabbedFits(ContextMenu): @@ -51,17 +51,14 @@ class TabbedFits(ContextMenu): return m def handleSelection(self, event): - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = self.fitLookup[event.Id] if self.context == 'commandView': - sFit.addCommandFit(fitID, fit) + self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(fitID, fit.ID)) elif self.context == 'projected': - sFit.project(fitID, fit) - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, fit.ID, 'fit')) TabbedFits.register() diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index e09b26940..94bfc1076 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -1,9 +1,9 @@ # noinspection PyPackageRequirements import wx -from gui.contextMenu import ContextMenu -import gui.mainFrame -import gui.globalEvents as GE +import gui.fitCommands as cmd +import gui.mainFrame +from gui.contextMenu import ContextMenu from service.fit import Fit from service.settings import ContextMenuSettings @@ -60,10 +60,8 @@ class TacticalMode(ContextMenu): event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - sFit.setMode(fitID, self.modeIds[event.Id]) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiSetModeCommand(fitID, self.modeIds[event.Id])) TacticalMode.register() diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index bafca94ab..5b824e8eb 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -1,14 +1,29 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import gui.globalEvents as GE +import re +from itertools import chain + # noinspection PyPackageRequirements import wx + +import gui.fitCommands as cmd +import gui.mainFrame +from gui.contextMenu import ContextMenu from service.market import Market -from service.fit import Fit from service.settings import ContextMenuSettings class WhProjector(ContextMenu): + + # CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect + # (which can be random when going into Abyssal space). This is how we currently define it: + # environment type: specific type name prefix + abyssal_mapping = { + 'caustic_toxin_weather': 47862, # Exotic Particle Storm + 'darkness_weather': 47863, # Dark Matter Field + 'infernal_weather': 47864, # Plasma Firestorm + 'electric_storm_weather': 47865, # Electrical Storm + 'xenon_gas_weather': 47866, # Gamma-Ray Afterglow + } + def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.settings = ContextMenuSettings.getInstance() @@ -20,32 +35,49 @@ class WhProjector(ContextMenu): return srcContext == "projected" def getText(self, itmContext, selection): - return "Add System Effects" + return "Add Environmental Effect" def getSubMenu(self, context, selection, rootMenu, i, pitem): msw = True if "wxMSW" in wx.PlatformInfo else False - sMkt = Market.getInstance() - effdata = sMkt.getSystemWideEffects() + + # Wormholes self.idmap = {} sub = wx.Menu() - for swType in sorted(effdata): - subItem = wx.MenuItem(sub, wx.ID_ANY, swType) - grandSub = wx.Menu() - subItem.SetSubMenu(grandSub) - sub.Append(subItem) + wormhole_item = wx.MenuItem(sub, wx.ID_ANY, "Wormhole") + wormhole_menu = wx.Menu() + wormhole_item.SetSubMenu(wormhole_menu) + sub.Append(wormhole_item) + + grouped_data, flat_data = self.getEffectBeacons() + self.buildMenu(grouped_data, flat_data, wormhole_menu, rootMenu, msw) + + # Incursions + + grouped_data, flat_data = self.getEffectBeacons(incursions=True) + self.buildMenu(grouped_data, flat_data, sub, rootMenu, msw) + + # Abyssal Weather + + abyssal_item = wx.MenuItem(sub, wx.ID_ANY, "Abyssal Weather") + abyssal_menu = wx.Menu() + abyssal_item.SetSubMenu(abyssal_menu) + sub.Append(abyssal_item) + + grouped_data, flat_data = self.getAbyssalWeather() + self.buildMenu(grouped_data, flat_data, abyssal_menu, rootMenu, msw) + + # Localized Weather + + local_item = wx.MenuItem(sub, wx.ID_ANY, "Localized") + local_menu = wx.Menu() + local_item.SetSubMenu(local_menu) + sub.Append(local_item) + + grouped_data, flat_data = self.getLocalizedEnvironments() + self.buildMenu(grouped_data, flat_data, local_menu, rootMenu, msw) - for swData in sorted(effdata[swType], key=lambda tpl: tpl[2]): - wxid = ContextMenu.nextID() - swObj, swName, swClass = swData - self.idmap[wxid] = (swObj, swName) - grandSubItem = wx.MenuItem(grandSub, wxid, swClass) - if msw: - rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) - else: - grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) - grandSub.Append(grandSubItem) return sub def handleSelection(self, event): @@ -56,10 +88,139 @@ class WhProjector(ContextMenu): event.Skip() return - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - sFit.project(fitID, swObj) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, swObj.ID, 'item')) + + def buildMenu(self, grouped_data, flat_data, local_menu, rootMenu, msw): + + def processFlat(data, root, sub): + for swData in sorted(data, key=lambda tpl: tpl[2]): + wxid = ContextMenu.nextID() + swObj, swName, swClass = swData + self.idmap[wxid] = (swObj, swName) + subItem = wx.MenuItem(sub, wxid, swClass) + if msw: + root.Bind(wx.EVT_MENU, self.handleSelection, subItem) + else: + sub.Bind(wx.EVT_MENU, self.handleSelection, subItem) + sub.Append(subItem) + + for swType in sorted(grouped_data): + subItem = wx.MenuItem(local_menu, wx.ID_ANY, swType) + grandSub = wx.Menu() + subItem.SetSubMenu(grandSub) + local_menu.Append(subItem) + processFlat(grouped_data[swType], rootMenu, grandSub) + + processFlat(flat_data, rootMenu, local_menu) + + def getEffectBeacons(self, incursions=False): + """ + Get dictionary with wormhole system-wide effects + """ + sMkt = Market.getInstance() + + # todo: rework this + # Container for system-wide effects + grouped = {} + + # Expressions for matching when detecting effects we're looking for + if incursions: + validgroups = ("Incursion ship attributes effects",) + else: + validgroups = ("Black Hole Effect Beacon", + "Cataclysmic Variable Effect Beacon", + "Magnetar Effect Beacon", + "Pulsar Effect Beacon", + "Red Giant Beacon", + "Wolf Rayet Effect Beacon") + + # Stuff we don't want to see in names + garbages = ("Effect", "Beacon", "ship attributes effects") + + # Get group with all the system-wide beacons + grp = sMkt.getGroup("Effect Beacon") + + # Cycle through them + for beacon in sMkt.getItemsByGroup(grp): + # Check if it belongs to any valid group + for group in validgroups: + # Check beginning of the name only + if re.match(group, beacon.name): + # Get full beacon name + beaconname = beacon.name + for garbage in garbages: + beaconname = re.sub(garbage, "", beaconname) + beaconname = re.sub(" {2,}", " ", beaconname).strip() + # Get short name + shortname = re.sub(group, "", beacon.name) + for garbage in garbages: + shortname = re.sub(garbage, "", shortname) + shortname = re.sub(" {2,}", " ", shortname).strip() + # Get group name + groupname = group + for garbage in garbages: + groupname = re.sub(garbage, "", groupname) + groupname = re.sub(" {2,}", " ", groupname).strip() + # Add stuff to dictionary + if groupname not in grouped: + grouped[groupname] = set() + grouped[groupname].add((beacon, beaconname, shortname)) + # Break loop on 1st result + break + + return grouped, () + + def getAbyssalWeather(self): + sMkt = Market.getInstance() + + environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items} + items = chain(sMkt.getGroup("MassiveEnvironments").items, sMkt.getGroup("Non-Interactable Object").items) + + grouped = {} + flat = set() + + for beacon in items: + if not beacon.isType('projected'): + continue + + type = self.__class__.abyssal_mapping.get(beacon.name[0:-2], None) + type = environments.get(type, None) + if type is None: + continue + + if type.name not in grouped: + grouped[type.name] = set() + + display_name = "{} {}".format(type.name, beacon.name[-1:]) + grouped[type.name].add((beacon, display_name, display_name)) + + # PVP weather + flat.add((sMkt.getItem(49766), 'PvP Weather', 'PvP Weather')) + + return grouped, flat + + def getLocalizedEnvironments(self): + sMkt = Market.getInstance() + + grp = sMkt.getGroup("Abyssal Hazards") + + grouped = dict() + + for beacon in grp.items: + if not beacon.isType('projected'): + continue + # Localized effects, currently, have a name like "(size) (type) Cloud" + # Until this inevitably changes, do a simple split + name_parts = beacon.name.split(" ") + + key = name_parts[1].strip() + if key not in grouped: + grouped[key] = set() + + grouped[key].add((beacon, beacon.name, beacon.name)) + + return grouped, () WhProjector.register() diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index d848d3f9c..183bc9ac8 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -55,7 +55,7 @@ class FitDpsGraph(Graph): icons = {} sAttr = Attribute.getInstance() for key, attrName in self.propertyAttributeMap.items(): - iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile + iconFile = sAttr.getAttributeInfo(attrName).iconID bitmap = BitmapLoader.getBitmap(iconFile, "icons") if bitmap: icons[key] = bitmap diff --git a/gui/builtinItemStatsViews/attributeSlider.py b/gui/builtinItemStatsViews/attributeSlider.py new file mode 100644 index 000000000..9e3585ea9 --- /dev/null +++ b/gui/builtinItemStatsViews/attributeSlider.py @@ -0,0 +1,261 @@ +import math + +import wx +import wx.lib.newevent + +from gui.attribute_gauge import AttributeGauge +from eos.utils.float import floatUnerr + +_ValueChanged, EVT_VALUE_CHANGED = wx.lib.newevent.NewEvent() + + +class AttributeSliderChangeEvent: + def __init__(self, obj, old_value, new_value, old_percentage, new_percentage): + self.__obj = obj + self.__old = old_value + self.__new = new_value + self.__old_percent = old_percentage + self.__new_percent = new_percentage + + def GetObj(self): + return self.__obj + + def GetOldValue(self): + return self.__old + + def GetValue(self): + return self.__new + + def GetOldPercentage(self): + return self.__old_percent + + def GetPercentage(self): + return self.__new_percent + + Object = property(GetObj) + OldValue = property(GetOldValue) + Value = property(GetValue) + OldPercentage = property(GetOldPercentage) + Percentage = property(GetPercentage) + + +class ValueChanged(_ValueChanged, AttributeSliderChangeEvent): + def __init__(self, obj, old_value, new_value, old_percentage, new_percentage): + _ValueChanged.__init__(self) + AttributeSliderChangeEvent.__init__(self, obj, old_value, new_value, old_percentage, new_percentage) + + +class AttributeSlider(wx.Panel): + # Slider which abstracts users values from internal values (because the built in slider does not deal with floats + # and the like), based on http://wxpython-users.wxwidgets.narkive.com/ekgBzA7u/anyone-ever-thought-of-a-floating-point-slider + + def __init__(self, parent, baseValue, minValue, maxValue, inverse=False, id=-1): + wx.Panel.__init__(self, parent, id=id) + + self.parent = parent + + self.base_value = baseValue + + self.UserMinValue = minValue + self.UserMaxValue = maxValue + + self.inverse = inverse + + def getStep(valRange): + """ + Find step for the passed range, which is based on 1, 2 or 5. + Step returned will make sure that range fits 10..50 of them, + as close to 10 as possible. + """ + steps = {1: None, 2: None, 5: None} + for baseInc in steps: + baseIncAmount = valRange / baseInc + incScale = math.floor(math.log10(baseIncAmount) - 1) + steps[baseInc] = baseInc * 10 ** incScale + chosenBase = min(steps, key=lambda base: valRange / steps[base]) + chosenStep = steps[chosenBase] + if inverse: + chosenStep *= -1 + return chosenStep + + def getDigitPlaces(minValue, maxValue): + minDigits = 3 + maxDigits = 5 + currentDecision = minDigits + for value in (floatUnerr(minValue), floatUnerr(maxValue)): + for currentDigit in range(minDigits, maxDigits + 1): + if round(value, currentDigit) == value: + if currentDigit > currentDecision: + currentDecision = currentDigit + break + # Max decimal places we can afford to show was not enough + else: + return maxDigits + return currentDecision + + self.ctrl = wx.SpinCtrlDouble(self, min=minValue, max=maxValue, inc=getStep(maxValue - minValue)) + self.ctrl.SetDigits(getDigitPlaces(minValue, maxValue)) + + + self.ctrl.Bind(wx.EVT_SPINCTRLDOUBLE, self.UpdateValue) + + self.slider = AttributeGauge(self, size=(-1, 8)) + + b = 4 + vsizer1 = wx.BoxSizer(wx.VERTICAL) + vsizer1.Add(self.ctrl, 0, wx.LEFT | wx.RIGHT | wx.CENTER, b) + vsizer1.Add(self.slider, 0, wx.EXPAND | wx.ALL , b) + + self.SetSizerAndFit(vsizer1) + self.parent.SetClientSize((500, vsizer1.GetSize()[1])) + + def UpdateValue(self, evt): + self.SetValue(self.ctrl.GetValue()) + evt.Skip() + + def SetValue(self, value, post_event=True): + self.ctrl.SetValue(value) + invert_factor = -1 if self.inverse else 1 + if value >= self.base_value: + slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor + else: + slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor + self.slider.SetValue(slider_percentage) + if post_event: + wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage)) + + +class TestAttributeSlider(wx.Frame): + + def __init__(self, parent, id): + title = 'Slider...' + pos = wx.DefaultPosition + size = wx.DefaultSize + sty = wx.DEFAULT_FRAME_STYLE + wx.Frame.__init__(self, parent, id, title, pos, size, sty) + + self.panel = AttributeSlider(self, -50, 0.8, 1.5, False) + self.panel.Bind(EVT_VALUE_CHANGED, self.thing) + self.panel.SetValue(-55) + self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) + + def OnCloseWindow(self, event): + self.Destroy() + + def thing(self, evt): + print("thing") + + +if __name__ == "__main__": + app = wx.App() + frame = TestAttributeSlider(None, wx.ID_ANY) + frame.Show() + app.MainLoop() + + +# class AttributeSliderDEV(wx.Panel): +# # Slider which abstracts users values from internal values (because the built in slider does not deal with floats +# # and the like), based on http://wxpython-users.wxwidgets.narkive.com/ekgBzA7u/anyone-ever-thought-of-a-floating-point-slider +# +# def __init__(self, parent, baseValue, minMod, maxMod): +# wx.Panel.__init__(self, parent) +# +# self.parent = parent +# +# self.base_value = baseValue +# +# self.UserMinValue = minMod +# self.UserMaxValue = maxMod +# +# # The internal slider basically represents the percentage towards the end of the range. It has to be normalized +# # in this way, otherwise when we start off with a base, if the range is skewed to one side, the base value won't +# # be centered. We use a range of -100,100 so that we can depend on the SliderValue to contain the percentage +# # toward one end +# +# # Additionally, since we want the slider to be accurate to 3 decimal places, we need to blow out the two ends here +# # (if we have a slider that needs to land on 66.66% towards the right, it will actually be converted to 66%. Se we need it to support 6,666) +# +# self.SliderMinValue = -100_000 +# self.SliderMaxValue = 100_000 +# self.SliderValue = 0 +# +# self.statxt1 = wx.StaticText(self, wx.ID_ANY, 'left', +# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_LEFT) +# self.statxt2 = wx.StaticText(self, wx.ID_ANY, 'middle', +# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_CENTRE) +# self.statxt3 = wx.StaticText(self, wx.ID_ANY, 'right', +# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_RIGHT) +# +# self.statxt1.SetLabel("{0:.3f}".format(self.UserMinValue * self.base_value)) +# self.statxt1.SetToolTip("{0:+f}%".format((1-self.UserMinValue)*-100)) +# self.statxt2.SetLabel("{0:.3f}".format(self.base_value)) +# self.statxt3.SetLabel("{0:.3f}".format(self.UserMaxValue * self.base_value)) +# self.statxt3.SetToolTip("{0:+f}%".format((1-self.UserMaxValue)*-100)) +# +# self.slider = wx.Slider( +# self, wx.ID_ANY, +# self.SliderValue, +# self.SliderMinValue, +# self.SliderMaxValue, +# style=wx.SL_HORIZONTAL) +# +# self.slider.SetTickFreq((self.SliderMaxValue - self.SliderMinValue) / 15) +# +# self.slider.Bind(wx.EVT_SCROLL, self.OnScroll) +# +# b = 20 +# hsizer1 = wx.BoxSizer(wx.HORIZONTAL) +# hsizer1.Add(self.statxt1, 1, wx.RIGHT, b) +# hsizer1.Add(self.statxt2, 1, wx.LEFT | wx.RIGHT, b) +# hsizer1.Add(self.statxt3, 1, wx.LEFT, b) +# +# b = 4 +# vsizer1 = wx.BoxSizer(wx.VERTICAL) +# vsizer1.Add(hsizer1, 0, wx.EXPAND | wx.ALL, b) +# vsizer1.Add(self.slider, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, b) +# +# self.SetSizerAndFit(vsizer1) +# self.parent.SetClientSize((500, vsizer1.GetSize()[1])) +# +# def OnScroll(self, event): +# self.CalculateUserValue() +# +# def SetValue(self, value): +# # todo: check this against values that might be 2.5x and whatnot +# mod = value / self.base_value +# slider_percentage = 0 +# if mod < 1: +# modEnd = -1 * self.UserMinValue +# slider_percentage = (modEnd / mod) * 10_000 +# elif mod > 1: +# modEnd = self.UserMaxValue +# slider_percentage = ((mod-1)/(modEnd-1)) * 100_000 +# +# self.slider.SetValue(slider_percentage) +# self.CalculateUserValue() +# +# def CalculateUserValue(self): +# self.SliderValue = self.slider.GetValue() +# +# mod = 1 +# +# # The slider value tells us when mod we're going to use, depending on its sign +# if self.SliderValue < 0: +# mod = self.UserMinValue +# elif self.SliderValue > 0: +# mod = self.UserMaxValue +# +# # Get the slider value percentage as an absolute value +# slider_mod = abs(self.SliderValue/1_000) / 100 +# +# # Gets our new mod by use the slider's percentage to determine where in the spectrum it is +# new_mod = mod + ((1 - mod) - ((1 - mod) * slider_mod)) +# +# # Modifies our base value, to get out modified value +# newValue = new_mod * self.base_value +# +# if mod == 1: +# self.statxt2.SetLabel("{0:.3f}".format(newValue)) +# else: +# self.statxt2.SetLabel("{0:.3f} ({1:+.3f})".format(newValue, newValue - self.base_value, )) +# self.statxt2.SetToolTip("{0:+f}%".format(new_mod*100)) diff --git a/gui/builtinItemStatsViews/itemAffectedBy.py b/gui/builtinItemStatsViews/itemAffectedBy.py index ed4280045..94808b734 100644 --- a/gui/builtinItemStatsViews/itemAffectedBy.py +++ b/gui/builtinItemStatsViews/itemAffectedBy.py @@ -237,16 +237,16 @@ class ItemAffectedBy(wx.Panel): displayName = attrInfo.displayName if attrInfo and attrInfo.displayName != "" else attrName if attrInfo: - if attrInfo.icon is not None: - iconFile = attrInfo.icon.iconFile + if attrInfo.iconID is not None: + iconFile = attrInfo.iconID icon = BitmapLoader.getBitmap(iconFile, "icons") if icon is None: icon = BitmapLoader.getBitmap("transparent16x16", "gui") attrIcon = self.imageList.Add(icon) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) if self.showRealNames: display = attrName @@ -267,8 +267,8 @@ class ItemAffectedBy(wx.Panel): if afflictorType == Ship: itemIcon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui")) - elif item.icon: - bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons") + elif item.iconID: + bitmap = BitmapLoader.getBitmap(item.iconID, "icons") itemIcon = self.imageList.Add(bitmap) if bitmap else -1 else: itemIcon = -1 @@ -373,8 +373,8 @@ class ItemAffectedBy(wx.Panel): counter = len(afflictors) if afflictorType == Ship: itemIcon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui")) - elif item.icon: - bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons") + elif item.iconID: + bitmap = BitmapLoader.getBitmap(item.iconID, "icons") itemIcon = self.imageList.Add(bitmap) if bitmap else -1 else: itemIcon = -1 @@ -398,17 +398,17 @@ class ItemAffectedBy(wx.Panel): displayName = attrInfo.displayName if attrInfo else "" if attrInfo: - if attrInfo.icon is not None: - iconFile = attrInfo.icon.iconFile + if attrInfo.iconID is not None: + iconFile = attrInfo.iconID icon = BitmapLoader.getBitmap(iconFile, "icons") if icon is None: icon = BitmapLoader.getBitmap("transparent16x16", "gui") attrIcon = self.imageList.Add(icon) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) penalized = "" if '*' in attrModifier: diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index e40710c45..45eb6ec04 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -7,9 +7,7 @@ import wx from .helpers import AutoListCtrl from gui.bitmap_loader import BitmapLoader -from service.market import Market -from service.attribute import Attribute -from gui.utils.numberFormatter import formatAmount +from gui.utils.numberFormatter import formatAmount, roundDec class ItemParams(wx.Panel): @@ -86,7 +84,8 @@ class ItemParams(wx.Panel): def RefreshValues(self, event): self._fetchValues() self.UpdateList() - event.Skip() + if event: + event.Skip() def ToggleViewMode(self, event): self.toggleView *= -1 @@ -102,7 +101,7 @@ class ItemParams(wx.Panel): if saveFileDialog.ShowModal() == wx.ID_CANCEL: return # the user hit cancel... - with open(saveFileDialog.GetPath(), "wb") as exportFile: + with open(saveFileDialog.GetPath(), "w") as exportFile: writer = csv.writer(exportFile, delimiter=',') writer.writerow( @@ -174,7 +173,12 @@ class ItemParams(wx.Panel): info = self.attrInfo.get(name) att = self.attrValues[name] - valDefault = getattr(info, "value", None) + # If we're working with a stuff object, we should get the original value from our getBaseAttrValue function, + # which will return the value with respect to the effective base (with mutators / overrides in place) + valDefault = getattr(info, "value", None) # Get default value from attribute + if self.stuff is not None: + # if it's a stuff, overwrite default (with fallback to current value) + valDefault = self.stuff.getBaseAttrValue(name, valDefault) valueDefault = valDefault if valDefault is not None else att val = getattr(att, "value", None) @@ -189,8 +193,8 @@ class ItemParams(wx.Panel): attrName += " ({})".format(info.ID) if info: - if info.icon is not None: - iconFile = info.icon.iconFile + if info.iconID is not None: + iconFile = info.iconID icon = BitmapLoader.getBitmap(iconFile, "icons") if icon is None: @@ -198,9 +202,9 @@ class ItemParams(wx.Panel): attrIcon = self.imageList.Add(icon) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) else: - attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) + attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons")) index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon) idNameMap[idCount] = attrName @@ -210,14 +214,14 @@ class ItemParams(wx.Panel): if self.toggleView != 1: valueUnit = str(value) elif info and info.unit: - valueUnit = self.TranslateValueUnit(value, info.unit.displayName, info.unit.name) + valueUnit = self.FormatValue(*info.unit.PreformatValue(value)) else: valueUnit = formatAmount(value, 3, 0, 0) if self.toggleView != 1: valueUnitDefault = str(valueDefault) elif info and info.unit: - valueUnitDefault = self.TranslateValueUnit(valueDefault, info.unit.displayName, info.unit.name) + valueUnitDefault = self.FormatValue(*info.unit.PreformatValue(valueDefault)) else: valueUnitDefault = formatAmount(valueDefault, 3, 0, 0) @@ -232,44 +236,13 @@ class ItemParams(wx.Panel): self.Layout() @staticmethod - def TranslateValueUnit(value, unitName, unitDisplayName): - def itemIDCallback(): - item = Market.getInstance().getItem(value) - return "%s (%d)" % (item.name, value) if item is not None else str(value) - - def groupIDCallback(): - group = Market.getInstance().getGroup(value) - return "%s (%d)" % (group.name, value) if group is not None else str(value) - - def attributeIDCallback(): - if not value: # some attributes come through with a value of 0? See #1387 - return "%d" % (value) - attribute = Attribute.getInstance().getAttributeInfo(value) - return "%s (%d)" % (attribute.name.capitalize(), value) - - trans = { - "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, "m\u00B3"), - "Sizeclass" : (lambda: value, ""), - "Absolute Percent" : (lambda: (value * 100), unitName), - "Milliseconds" : (lambda: value / 1000.0, unitName), - "typeID" : (itemIDCallback, ""), - "groupID" : (groupIDCallback, ""), - "attributeID" : (attributeIDCallback, "") - } - - override = trans.get(unitDisplayName) - if override is not None: - v = override[0]() - if isinstance(v, str): - fvalue = v - elif isinstance(v, (int, float)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v - return "%s %s" % (fvalue, override[1]) + def FormatValue(value, unit, rounding='prec', digits=3): + """Formats a value / unit combination into a string + @todo: move this to a more central location, since this is also used in the item mutator panel""" + if isinstance(value, (int, float)) and rounding == 'prec': + fvalue = formatAmount(value, digits, 0, 0) + elif isinstance(value, (int, float)) and rounding == 'dec': + fvalue = roundDec(value, digits) else: - return "%s %s" % (formatAmount(value, 3, 0), unitName) + fvalue = value + return "%s %s" % (fvalue, unit) diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py index 9e9ca53af..97a4b953d 100644 --- a/gui/builtinItemStatsViews/itemCompare.py +++ b/gui/builtinItemStatsViews/itemCompare.py @@ -8,6 +8,10 @@ from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount +def defaultSort(item): + return (item.attributes['metaLevel'].value if 'metaLevel' in item.attributes else 0, item.name) + + class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): # Start dealing with Price stuff to get that thread going @@ -27,8 +31,7 @@ class ItemCompare(wx.Panel): self.currentSort = None self.sortReverse = False self.item = item - self.items = sorted(items, - key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else 0) + self.items = sorted(items, key=defaultSort) self.attrs = {} # get a dict of attrName: attrInfo of all unique attributes across all items @@ -125,11 +128,16 @@ class ItemCompare(wx.Panel): # Remember to reduce by 1, because the attrs array # starts at 0 while the list has the item name as column 0. attr = str(list(self.attrs.keys())[sort - 1]) - func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None + func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else 0.0 + # Clicked on a column that's not part of our array (price most likely) except IndexError: - # Clicked on a column that's not part of our array (price most likely) - self.sortReverse = False - func = lambda _val: _val.attributes['metaLevel'].value if 'metaLevel' in _val.attributes else None + # Price + if sort == len(self.attrs) + 1: + func = lambda i: i.price.price if i.price.price != 0 else float("Inf") + # Something else + else: + self.sortReverse = False + func = defaultSort self.items = sorted(self.items, key=func, reverse=self.sortReverse) @@ -160,7 +168,7 @@ class ItemCompare(wx.Panel): self.paramList.SetItem(i, x + 1, valueUnit) # Add prices - self.paramList.SetItem(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) if item.price.price else "") self.paramList.RefreshRows() self.Layout() diff --git a/gui/builtinItemStatsViews/itemDependants.py b/gui/builtinItemStatsViews/itemDependants.py index 9f7e459c9..84b8f8997 100644 --- a/gui/builtinItemStatsViews/itemDependants.py +++ b/gui/builtinItemStatsViews/itemDependants.py @@ -44,8 +44,8 @@ class ItemDependents(wx.Panel): child = self.reqTree.AppendItem(parent, "Level {}".format(self.romanNb[int(x)]), sbIconId) for item in items: - if item.icon: - bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons") + if item.iconID: + bitmap = BitmapLoader.getBitmap(item.iconID, "icons") itemIcon = self.imageList.Add(bitmap) if bitmap else -1 else: itemIcon = -1 diff --git a/gui/builtinItemStatsViews/itemDescription.py b/gui/builtinItemStatsViews/itemDescription.py index ddf30dfb4..9c62dcc78 100644 --- a/gui/builtinItemStatsViews/itemDescription.py +++ b/gui/builtinItemStatsViews/itemDescription.py @@ -15,7 +15,6 @@ class ItemDescription(wx.Panel): fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) self.description = wx.html.HtmlWindow(self) - if not item.description: return @@ -24,10 +23,46 @@ class ItemDescription(wx.Panel): desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P.*?)<( *)/( *)font( *)>", "\g", desc) # Strip URLs desc = re.sub("<( *)a(.*?)>(?P.*?)<( *)/( *)a( *)>", "\g", desc) - desc = "" + desc + "" + desc = "{}".format( + bgcolor.GetAsString(wx.C2S_CSS_SYNTAX), + fgcolor.GetAsString(wx.C2S_CSS_SYNTAX), + desc + ) self.description.SetPage(desc) mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) self.Layout() + + self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu) + self.description.Bind(wx.EVT_KEY_DOWN, self.onKeyDown) + + self.popupMenu = wx.Menu() + copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy') + self.popupMenu.Append(copyItem) + self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem) + + def onPopupMenu(self, event): + self.PopupMenu(self.popupMenu) + + def menuClickHandler(self, event): + selectedMenuItem = event.GetId() + if selectedMenuItem == 1: # Copy was chosen + self.copySelectionToClipboard() + + def onKeyDown(self, event): + keyCode = event.GetKeyCode() + # Ctrl + C + if keyCode == 67 and event.ControlDown(): + self.copySelectionToClipboard() + # Ctrl + A + if keyCode == 65 and event.ControlDown(): + self.description.SelectAll() + + def copySelectionToClipboard(self): + selectedText = self.description.SelectionToText() + if selectedText == '': # if no selection, copy all content + selectedText = self.description.ToText() + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(wx.TextDataObject(selectedText)) + wx.TheClipboard.Close() diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py index e586786a6..37b37c608 100644 --- a/gui/builtinItemStatsViews/itemEffects.py +++ b/gui/builtinItemStatsViews/itemEffects.py @@ -45,7 +45,7 @@ class ItemEffects(wx.Panel): self.effectList.SetColumnWidth(4, 40) item = self.item - effects = item.effects + self.effects = effects = item.effects names = list(effects.keys()) names.sort() @@ -100,14 +100,15 @@ class ItemEffects(wx.Panel): self.RefreshValues(event) - @staticmethod - def OnRightClick(event): + def OnRightClick(self, event): """ Debug use: open effect file with default application. If effect file does not exist, create it """ - file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower()) + effect = self.effects[event.GetText()] + + file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % effect.handlerName) if not os.path.isfile(file_): open(file_, 'a').close() diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py new file mode 100644 index 000000000..dc91453fa --- /dev/null +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -0,0 +1,193 @@ +# noinspection PyPackageRequirements +import random + +import wx +from logbook import Logger + +import gui.globalEvents as GE +import gui.mainFrame +from gui.bitmap_loader import BitmapLoader +from service.fit import Fit +from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED +from .itemAttributes import ItemParams + +pyfalog = Logger(__name__) + + +class ItemMutator(wx.Panel): + + def __init__(self, parent, stuff, item): + wx.Panel.__init__(self, parent) + self.stuff = stuff + self.item = item + self.timer = None + self.activeFit = gui.mainFrame.MainFrame.getInstance().getActiveFit() + + font = parent.GetFont() + font.SetWeight(wx.BOLD) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + sourceItemsSizer = wx.BoxSizer(wx.HORIZONTAL) + sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons"), 0, wx.LEFT, 5) + sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons"), 0, wx.LEFT, 0) + sourceItemShort = "{} {}".format(stuff.mutaplasmid.item.name.split(" ")[0], stuff.baseItem.name) + sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort) + sourceItemText.SetFont(font) + sourceItemsSizer.Add(sourceItemText, 0, wx.LEFT, 10) + mainSizer.Add(sourceItemsSizer, 0, wx.TOP | wx.EXPAND, 10) + + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5) + + self.goodColor = wx.Colour(96, 191, 0) + self.badColor = wx.Colour(255, 64, 0) + + self.event_mapping = {} + + for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName): + # Format: [raw value, modifier applied to base raw value, display value] + range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue)) + range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue)) + + # minValue/maxValue do not always correspond to min/max, because these are + # just base value multiplied by minMod/maxMod, and in case base is negative + # minValue is actually bigger than maxValue + if range1[0] <= range2[0]: + minRange = range1 + maxRange = range2 + else: + minRange = range2 + maxRange = range1 + + if (m.highIsGood and minRange[0] >= maxRange[0]) or (not m.highIsGood and minRange[0] <= maxRange[0]): + betterRange = minRange + worseRange = maxRange + else: + betterRange = maxRange + worseRange = minRange + + if minRange[1] >= maxRange[1]: + displayMaxRange = minRange + displayMinRange = maxRange + else: + displayMaxRange = maxRange + displayMinRange = minRange + + # If base value is outside of mutation range, make sure that center of slider + # corresponds to the value which is closest available to actual base value. It's + # how EVE handles it + if minRange[0] <= m.baseValue <= maxRange[0]: + sliderBaseValue = m.baseValue + else: + sliderBaseValue = max(minRange[0], min(maxRange[0], m.baseValue)) + + headingSizer = wx.BoxSizer(wx.HORIZONTAL) + + headingSizer.Add(BitmapLoader.getStaticBitmap(m.attribute.iconID, self, "icons"), 0, wx.RIGHT, 10) + + displayName = wx.StaticText(self, wx.ID_ANY, m.attribute.displayName) + displayName.SetFont(font) + + headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0) + + worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec') + worseText = wx.StaticText(self, wx.ID_ANY, worseVal) + worseText.SetForegroundColour(self.badColor) + + betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec') + betterText = wx.StaticText(self, wx.ID_ANY, betterVal) + betterText.SetForegroundColour(self.goodColor) + + headingSizer.Add(worseText, 0, wx.ALL | wx.EXPAND, 0) + headingSizer.Add(wx.StaticText(self, wx.ID_ANY, " ─ "), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5) + headingSizer.Add(betterText, 0, wx.RIGHT | wx.EXPAND, 10) + + mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5) + + slider = AttributeSlider(parent=self, + baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue), + minValue=displayMinRange[1], + maxValue=displayMaxRange[1], + inverse=displayMaxRange is worseRange) + slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False) + slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue) + self.event_mapping[slider] = m + mainSizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10) + + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5) + + mainSizer.AddStretchSpacer() + + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + + bSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.resetMutatedValues) + + self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.randomBtn.Bind(wx.EVT_BUTTON, self.randomMutatedValues) + + mainSizer.Add(bSizer, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 0) + + self.SetSizer(mainSizer) + self.Layout() + + def changeMutatedValue(self, evt): + m = self.event_mapping[evt.Object] + value = evt.Value + value = m.attribute.unit.ComplicateValue(value) + sFit = Fit.getInstance() + + sFit.changeMutatedValue(m, value) + if self.timer: + self.timer.Stop() + self.timer = None + + for x in self.Parent.Children: + if isinstance(x, ItemParams): + x.RefreshValues(None) + break + self.timer = wx.CallLater(1000, self.callLater) + + def resetMutatedValues(self, evt): + sFit = Fit.getInstance() + + for slider, m in self.event_mapping.items(): + value = sFit.changeMutatedValue(m, m.baseValue) + value = m.attribute.unit.SimplifyValue(value) + slider.SetValue(value) + + evt.Skip() + + def randomMutatedValues(self, evt): + sFit = Fit.getInstance() + + for slider, m in self.event_mapping.items(): + value = random.uniform(m.minValue, m.maxValue) + value = sFit.changeMutatedValue(m, value) + value = m.attribute.unit.SimplifyValue(value) + slider.SetValue(value) + + evt.Skip() + + def callLater(self): + self.timer = None + sFit = Fit.getInstance() + + # recalc the fit that this module affects. This is not necessarily the currently active fit + sFit.refreshFit(self.activeFit) + + mainFrame = gui.mainFrame.MainFrame.getInstance() + activeFit = mainFrame.getActiveFit() + + if activeFit != self.activeFit: + # if we're no longer on the fit this module is affecting, simulate a "switch fit" so that the active fit + # can be recalculated (if needed) + sFit.switchFit(activeFit) + + # Send signal to GUI to update stats with current active fit + wx.PostEvent(mainFrame, GE.FitChanged(fitID=activeFit)) diff --git a/gui/builtinItemStatsViews/itemTraits.py b/gui/builtinItemStatsViews/itemTraits.py index 12abd078d..1ea0514a5 100644 --- a/gui/builtinItemStatsViews/itemTraits.py +++ b/gui/builtinItemStatsViews/itemTraits.py @@ -13,5 +13,38 @@ class ItemTraits(wx.Panel): self.traits = wx.html.HtmlWindow(self) self.traits.SetPage(item.traits.traitText) + self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu) + self.traits.Bind(wx.EVT_KEY_DOWN, self.onKeyDown) + mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0) self.Layout() + + self.popupMenu = wx.Menu() + copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy') + self.popupMenu.Append(copyItem) + self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem) + + def onPopupMenu(self, event): + self.PopupMenu(self.popupMenu) + + def menuClickHandler(self, event): + selectedMenuItem = event.GetId() + if selectedMenuItem == 1: # Copy was chosen + self.copySelectionToClipboard() + + def onKeyDown(self, event): + keyCode = event.GetKeyCode() + # Ctrl + C + if keyCode == 67 and event.ControlDown(): + self.copySelectionToClipboard() + # Ctrl + A + if keyCode == 65 and event.ControlDown(): + self.traits.SelectAll() + + def copySelectionToClipboard(self): + selectedText = self.traits.SelectionToText() + if selectedText == '': # if no selection, copy all content + selectedText = self.traits.ToText() + if wx.TheClipboard.Open(): + wx.TheClipboard.SetData(wx.TextDataObject(selectedText)) + wx.TheClipboard.Close() diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index 7a26cfed1..82454502a 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -1,16 +1,13 @@ import wx - -import config -import gui.builtinMarketBrowser.pfSearchBox as SBox -from gui.contextMenu import ContextMenu -from gui.display import Display -from service.attribute import Attribute -from service.fit import Fit -from gui.utils.staticHelpers import DragDropHelper - from logbook import Logger -from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, MAX_RECENTLY_USED_MODULES, ItemSelected +import gui.builtinMarketBrowser.pfSearchBox as SBox +from gui.builtinMarketBrowser.events import ItemSelected, MAX_RECENTLY_USED_MODULES, RECENTLY_USED_MODULES +from gui.contextMenu import ContextMenu +from gui.display import Display +from gui.utils.staticHelpers import DragDropHelper +from service.attribute import Attribute +from service.fit import Fit pyfalog = Logger(__name__) diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py index 8b1286e50..f6c62732c 100644 --- a/gui/builtinMarketBrowser/marketTree.py +++ b/gui/builtinMarketBrowser/marketTree.py @@ -53,6 +53,7 @@ class MarketTree(wx.TreeCtrl): # And add real market group contents sMkt = self.sMkt currentMktGrp = sMkt.getMarketGroup(self.GetItemData(root), eager="children") + for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): # If market should have items but it doesn't, do not show it if sMkt.marketGroupValidityCheck(childMktGrp) is False: diff --git a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py index 458424dce..7c767c5fb 100644 --- a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py +++ b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py @@ -81,6 +81,11 @@ class PFContextMenuPref(PreferenceView): rbSizerRow3.Add(self.rbBox7, 1, wx.TOP | wx.RIGHT, 5) self.rbBox7.Bind(wx.EVT_RADIOBOX, self.OnSetting7Change) + self.rbBox8 = wx.RadioBox(panel, -1, "Fill with module", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS) + self.rbBox8.SetSelection(self.settings.get('moduleFill')) + rbSizerRow3.Add(self.rbBox8, 1, wx.TOP | wx.RIGHT, 5) + self.rbBox8.Bind(wx.EVT_RADIOBOX, self.OnSetting8Change) + mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0) panel.SetSizer(mainSizer) @@ -107,6 +112,9 @@ class PFContextMenuPref(PreferenceView): def OnSetting7Change(self, event): self.settings.set('project', event.GetInt()) + def OnSetting8Change(self, event): + self.settings.set('moduleFill', event.GetInt()) + def getImage(self): return BitmapLoader.getBitmap("settings_menu", "gui") diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py index 815e1b124..c41921393 100644 --- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py +++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py @@ -4,8 +4,10 @@ import wx from service.fit import Fit from gui.bitmap_loader import BitmapLoader +import gui.globalEvents as GE from gui.preferenceView import PreferenceView from service.settings import EOSSettings +import gui.mainFrame logger = logging.getLogger(__name__) @@ -21,6 +23,7 @@ class PFFittingEnginePref(PreferenceView): # noinspection PyAttributeOutsideInit def populatePanel(self, panel): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -98,6 +101,9 @@ class PFFittingEnginePref(PreferenceView): def OnCBGlobalForceReloadStateChange(self, event): self.sFit.serviceFittingOptions["useGlobalForceReload"] = self.cbGlobalForceReload.GetValue() + fitID = self.mainFrame.getActiveFit() + self.sFit.refreshFit(fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def OnCBStrictSkillLevelsChange(self, event): self.engine_settings.set("strictSkillLevels", self.cbStrictSkillLevels.GetValue()) diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py index 029003a83..39125ed38 100644 --- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -1,17 +1,13 @@ # noinspection PyPackageRequirements import wx -from gui.preferenceView import PreferenceView -from gui.bitmap_loader import BitmapLoader - import gui.mainFrame - +from gui.bitmap_loader import BitmapLoader +from gui.preferenceView import PreferenceView from service.settings import EsiSettings -# noinspection PyPackageRequirements -from wx.lib.intctrl import IntCtrl -from service.esi import Esi +# noinspection PyPackageRequirements class PFEsiPref(PreferenceView): @@ -43,23 +39,65 @@ class PFEsiPref(PreferenceView): rbSizer = wx.BoxSizer(wx.HORIZONTAL) self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize, ['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS) - self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to with information about the character login.") - self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application to allow for character login. Use this if having issues with the local server.") - # self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, - # ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) + self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to" + " with information about the character login.") + self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application " + "to allow for character login. Use this if having issues with the local server.") + + self.rbSsoMode = wx.RadioBox(panel, -1, "SSO Mode", wx.DefaultPosition, wx.DefaultSize, + ['pyfa.io', 'Custom application'], 1, wx.RA_SPECIFY_COLS) + self.rbSsoMode.SetItemToolTip(0, "This options routes SSO Logins through pyfa.io, allowing you to easily login " + "without any configuration. When in doubt, use this option.") + self.rbSsoMode.SetItemToolTip(1, "This option goes through EVE SSO directly, but requires more configuration. Use " + "this is pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io.") self.rbMode.SetSelection(self.settings.get('loginMode')) - # self.rbServer.SetSelection(self.settings.get('server')) + self.rbSsoMode.SetSelection(self.settings.get('ssoMode')) + rbSizer.Add(self.rbSsoMode, 1, wx.ALL, 5) rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) - # rbSizer.Add(self.rbServer, 1, wx.ALL, 5) self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) - # self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) + self.rbSsoMode.Bind(wx.EVT_RADIOBOX, self.OnSSOChange) mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) - timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) + detailsTitle = wx.StaticText(panel, wx.ID_ANY, "Custom Application", wx.DefaultPosition, wx.DefaultSize, 0) + detailsTitle.Wrap(-1) + detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + + mainSizer.Add(detailsTitle, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND, 5) + + fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + fgAddrSizer.AddGrowableCol(1) + fgAddrSizer.SetFlexibleDirection(wx.BOTH) + fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + + self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetID.Wrap(-1) + fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, + wx.DefaultSize, 0) + + fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + + self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetSecret.Wrap(-1) + + fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, + wx.DefaultSize, 0) + + fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + + self.inputClientID.Bind(wx.EVT_TEXT, self.OnClientDetailChange) + self.inputClientSecret.Bind(wx.EVT_TEXT, self.OnClientDetailChange) + + mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) # self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) # self.stTimout.Wrap(-1) @@ -112,6 +150,7 @@ class PFEsiPref(PreferenceView): # self.ToggleProxySettings(self.settings.get('loginMode')) + self.ToggleSSOMode(self.settings.get('ssoMode')) panel.SetSizer(mainSizer) panel.Layout() @@ -121,14 +160,31 @@ class PFEsiPref(PreferenceView): def OnModeChange(self, event): self.settings.set('loginMode', event.GetInt()) - def OnServerChange(self, event): - self.settings.set('server', event.GetInt()) + def OnSSOChange(self, event): + self.settings.set('ssoMode', event.GetInt()) + self.ToggleSSOMode(event.GetInt()) - def OnBtnApply(self, event): + def ToggleSSOMode(self, mode): + if mode: + self.stSetID.Enable() + self.inputClientID.Enable() + self.stSetSecret.Enable() + self.inputClientSecret.Enable() + self.rbMode.Disable() + else: + self.stSetID.Disable() + self.inputClientID.Disable() + self.stSetSecret.Disable() + self.inputClientSecret.Disable() + self.rbMode.Enable() + + def OnClientDetailChange(self, evt): self.settings.set('clientID', self.inputClientID.GetValue().strip()) self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sEsi = Esi.getInstance() - sEsi.delAllCharacters() + + # sEsi = Esi.getInstance() + # sEsi.delAllCharacters() + # def getImage(self): return BitmapLoader.getBitmap("eve", "gui") diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index c0ca68a98..7ba91eff3 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -3,12 +3,9 @@ # noinspection PyPackageRequirements import wx -import copy -from gui.preferenceView import PreferenceView from gui.bitmap_loader import BitmapLoader -from gui.utils.color import CalculateTransition -import gui.utils.draw as drawUtils +from gui.preferenceView import PreferenceView ########################################################################### diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 412e1a988..51d32ab5d 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -38,6 +38,10 @@ class PFGeneralPref(PreferenceView): 0) mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5) + self.cbDefaultCharImplants = wx.CheckBox(panel, wx.ID_ANY, "Use character implants by default for new fits", + wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbDefaultCharImplants, 0, wx.ALL | wx.EXPAND, 5) + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, "Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) @@ -72,10 +76,6 @@ class PFGeneralPref(PreferenceView): self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, "Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5) - self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, "Export loaded charges", wx.DefaultPosition, - wx.DefaultSize, 0) - mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5) - self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, "Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) @@ -119,6 +119,7 @@ class PFGeneralPref(PreferenceView): self.sFit = Fit.getInstance() self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"]) + self.cbDefaultCharImplants.SetValue(self.sFit.serviceFittingOptions["useCharacterImplantsByDefault"]) self.cbGlobalDmgPattern.SetValue(self.sFit.serviceFittingOptions["useGlobalDamagePattern"]) self.cbFitColorSlots.SetValue(self.sFit.serviceFittingOptions["colorFitBySlot"] or False) self.cbRackSlots.SetValue(self.sFit.serviceFittingOptions["rackSlots"] or False) @@ -128,7 +129,6 @@ class PFGeneralPref(PreferenceView): self.cbShowTooltip.SetValue(self.sFit.serviceFittingOptions["showTooltip"] or False) self.cbMarketShortcuts.SetValue(self.sFit.serviceFittingOptions["showMarketShortcuts"] or False) self.cbGaugeAnimation.SetValue(self.sFit.serviceFittingOptions["enableGaugeAnimation"]) - self.cbExportCharges.SetValue(self.sFit.serviceFittingOptions["exportCharges"]) self.cbOpenFitInNew.SetValue(self.sFit.serviceFittingOptions["openFitInNew"]) self.chPriceSource.SetStringSelection(self.sFit.serviceFittingOptions["priceSource"]) self.chPriceSystem.SetStringSelection(self.sFit.serviceFittingOptions["priceSystem"]) @@ -136,6 +136,7 @@ class PFGeneralPref(PreferenceView): self.intDelay.SetValue(self.sFit.serviceFittingOptions["marketSearchDelay"]) self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange) + self.cbDefaultCharImplants.Bind(wx.EVT_CHECKBOX, self.OnCBDefaultCharImplantsStateChange) self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange) self.cbFitColorSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalColorBySlot) self.cbRackSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalRackSlots) @@ -145,7 +146,6 @@ class PFGeneralPref(PreferenceView): self.cbShowTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowTooltip) self.cbMarketShortcuts.Bind(wx.EVT_CHECKBOX, self.onCBShowShortcuts) self.cbGaugeAnimation.Bind(wx.EVT_CHECKBOX, self.onCBGaugeAnimation) - self.cbExportCharges.Bind(wx.EVT_CHECKBOX, self.onCBExportCharges) self.cbOpenFitInNew.Bind(wx.EVT_CHECKBOX, self.onCBOpenFitInNew) self.chPriceSource.Bind(wx.EVT_CHOICE, self.onPricesSourceSelection) self.chPriceSystem.Bind(wx.EVT_CHOICE, self.onPriceSelection) @@ -187,6 +187,10 @@ class PFGeneralPref(PreferenceView): self.sFit.serviceFittingOptions["useGlobalCharacter"] = self.cbGlobalChar.GetValue() event.Skip() + def OnCBDefaultCharImplantsStateChange(self, event): + self.sFit.serviceFittingOptions["useCharacterImplantsByDefault"] = self.cbDefaultCharImplants.GetValue() + event.Skip() + def OnCBGlobalDmgPatternStateChange(self, event): self.sFit.serviceFittingOptions["useGlobalDamagePattern"] = self.cbGlobalDmgPattern.GetValue() event.Skip() @@ -210,9 +214,6 @@ class PFGeneralPref(PreferenceView): def onCBGaugeAnimation(self, event): self.sFit.serviceFittingOptions["enableGaugeAnimation"] = self.cbGaugeAnimation.GetValue() - def onCBExportCharges(self, event): - self.sFit.serviceFittingOptions["exportCharges"] = self.cbExportCharges.GetValue() - def onCBOpenFitInNew(self, event): self.sFit.serviceFittingOptions["openFitInNew"] = self.cbOpenFitInNew.GetValue() diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index 4f9887f8a..526dc0930 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -150,10 +150,10 @@ class PFNetworkPref(PreferenceView): proxy = self.settings.autodetect() if proxy is not None: - addr, port = proxy - txt = addr + ":" + str(port) + addr, port = proxy + txt = addr + ":" + str(port) else: - txt = "None" + txt = "None" self.stPSAutoDetected.SetLabel("Auto-detected: " + txt) self.stPSAutoDetected.Disable() diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index 3d782fe5f..c0bd9c07b 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -2,28 +2,28 @@ import re import time -import config import wx from logbook import Logger +import config import gui.builtinShipBrowser.sfBrowserItem as SFItem -import gui.globalEvents as GE +import gui.fitCommands as cmd import gui.mainFrame import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from .events import ImportSelected, SearchSelected, FitSelected, BoosterListUpdated, Stage3Selected, FitRenamed, FitRemoved from gui.bitmap_loader import BitmapLoader from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame from service.fit import Fit +from .events import BoosterListUpdated, FitRemoved, FitSelected, ImportSelected, SearchSelected, Stage3Selected pyfalog = Logger(__name__) class FitItem(SFItem.SFBrowserItem): def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0, None), shipID=None, - itemData=None, + itemData=None, graphicID=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): @@ -51,7 +51,7 @@ class FitItem(SFItem.SFBrowserItem): self.deleted = False if shipID: - self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders") + self.shipBmp = BitmapLoader.getBitmap(str(graphicID), "renders") if not self.shipBmp: self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui") @@ -183,18 +183,14 @@ class FitItem(SFItem.SFBrowserItem): if activeFit: sFit = Fit.getInstance() projectedFit = sFit.getFit(self.fitID) - sFit.project(activeFit, projectedFit) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) - self.mainFrame.additionsPane.select("Projected") + if self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, projectedFit.ID, 'fit')): + self.mainFrame.additionsPane.select("Projected") def OnAddCommandFit(self, event): activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = Fit.getInstance() - commandFit = sFit.getFit(self.fitID) - sFit.addCommandFit(activeFit, commandFit) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) - self.mainFrame.additionsPane.select("Command") + if self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(activeFit, self.fitID)): + self.mainFrame.additionsPane.select("Command") def OnMouseCaptureLost(self, event): """ Destroy drag information (GH issue #479)""" @@ -325,14 +321,12 @@ class FitItem(SFItem.SFBrowserItem): self.Refresh() def renameFit(self, event=None): - sFit = Fit.getInstance() self.tcFitName.Show(False) self.editWasShown = 0 fitName = self.tcFitName.GetValue() if fitName: self.fitName = fitName - sFit.renameFit(self.fitID, self.fitName) - wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID)) + self.mainFrame.command.Submit(cmd.GuiFitRenameCommand(self.fitID, self.fitName)) else: self.tcFitName.SetValue(self.fitName) diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 16b7a4f24..bb30a040b 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -1,6 +1,7 @@ # noinspection PyPackageRequirements import wx import gui.utils.draw as drawUtils +import gui.mainFrame SB_ITEM_NORMAL = 0 SB_ITEM_SELECTED = 1 @@ -245,6 +246,7 @@ class SFBrowserItem(wx.Window): self.highlighted = False self.selected = False self.bkBitmap = None + self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.canBeDragged = False @@ -311,6 +313,10 @@ class SFBrowserItem(wx.Window): self.canBeDragged = mode def OnLeftUp(self, event): + if self.mainFrame.supress_left_up: + wx.Yield() + self.mainFrame.supress_left_up = False + return if self.HasCapture(): self.ReleaseMouse() diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index 4c679fb8a..cf1de024c 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -8,17 +8,17 @@ import gui.mainFrame import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from .events import Stage3Selected, Stage2Selected, Stage1Selected, FitSelected from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu from service.fit import Fit from service.market import Market +from .events import FitSelected, Stage3Selected pyfalog = Logger(__name__) class ShipItem(SFItem.SFBrowserItem): - def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None, + def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None, graphicID=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): SFItem.SFBrowserItem.__init__(self, parent, size=size) @@ -36,8 +36,8 @@ class ShipItem(SFItem.SFBrowserItem): self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) self.shipBmp = None - if shipID: - self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders") + if graphicID: + self.shipBmp = BitmapLoader.getBitmap(str(graphicID), "renders") if not self.shipBmp: self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui") diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 98d54d714..4c773c9f1 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -22,7 +22,8 @@ import wx import gui.mainFrame from gui.statsView import StatsView from gui.bitmap_loader import BitmapLoader -from gui.utils.numberFormatter import formatAmount +from gui.utils.numberFormatter import formatAmount, roundToPrec +from eos.utils.spoolSupport import SpoolType, SpoolOptions from service.fit import Fit @@ -129,7 +130,7 @@ class FirepowerViewFull(StatsView): # Remove effective label hsizer = self.headerPanel.GetSizer() hsizer.Hide(self.stEff) - #self.stEff.Destroy() + # self.stEff.Destroy() # Get the new view view = StatsView.getView("miningyieldViewFull")(self.parent) @@ -148,27 +149,55 @@ class FirepowerViewFull(StatsView): else: self.stEff.Hide() - stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS", None), - ("labelFullDpsDrone", lambda: fit.droneDPS, 3, 0, 0, "%s DPS", None), - ("labelFullVolleyTotal", lambda: fit.totalVolley, 3, 0, 0, "%s", "Volley: %.1f"), - ("labelFullDpsTotal", lambda: fit.totalDPS, 3, 0, 0, "%s", None)) - # See GH issue # - # if fit is not None and fit.totalYield > 0: - # self.miningyield.Show() - # else: - # self.miningyield.Hide() + def dpsToolTip(preSpool, fullSpool, prec, lowest, highest): + if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec): + return "" + else: + return "Spool up: {}-{}".format( + formatAmount(preSpool, prec, lowest, highest), + formatAmount(fullSpool, prec, lowest, highest)) + + # TODO: fetch spoolup option + defaultSpoolValue = 1 + stats = ( + ( + "labelFullDpsWeapon", + lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total, + lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total, + lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total, + 3, 0, 0, "{}{} DPS"), + ( + "labelFullDpsDrone", + lambda: fit.getDroneDps().total, + lambda: fit.getDroneDps().total, + lambda: fit.getDroneDps().total, + 3, 0, 0, "{}{} DPS"), + ( + "labelFullVolleyTotal", + lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total, + lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total, + lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total, + 3, 0, 0, "{}{}"), + ( + "labelFullDpsTotal", + lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total, + lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total, + lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total, + 3, 0, 0, "{}{}")) counter = 0 - for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: + for labelName, val, preSpoolVal, fullSpoolVal, prec, lowest, highest, valueFormat in stats: label = getattr(self, labelName) - value = value() if fit is not None else 0 - value = value if value is not None else 0 - if self._cachedValues[counter] != value: - valueStr = formatAmount(value, prec, lowest, highest) - label.SetLabel(valueFormat % valueStr) - tipStr = valueFormat % valueStr if altFormat is None else altFormat % value - label.SetToolTip(wx.ToolTip(tipStr)) - self._cachedValues[counter] = value + val = val() if fit is not None else 0 + preSpoolVal = preSpoolVal() if fit is not None else 0 + fullSpoolVal = fullSpoolVal() if fit is not None else 0 + if self._cachedValues[counter] != val: + tooltipText = dpsToolTip(preSpoolVal, fullSpoolVal, prec, lowest, highest) + label.SetLabel(valueFormat.format( + formatAmount(val, prec, lowest, highest), + "\u02e2" if tooltipText else "")) + label.SetToolTip(wx.ToolTip(tooltipText)) + self._cachedValues[counter] = val counter += 1 self.panel.Layout() diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index be24ee6af..3b6a6ce58 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -21,7 +21,35 @@ import wx from gui.statsView import StatsView from gui.bitmap_loader import BitmapLoader -from gui.utils.numberFormatter import formatAmount +from gui.utils.numberFormatter import formatAmount, roundToPrec +from eos.utils.spoolSupport import SpoolType, SpoolOptions + + +stats = [ + ( + "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Capacitor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Capacitor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Capacitor", 0), + 3, 0, 0), + ( + "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Shield", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Shield", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Shield", 0), + 3, 0, 0), + ( + "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Armor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Armor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Armor", 0), + 3, 0, 0), + ( + "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Hull", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Hull", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Hull", 0), + 3, 0, 0)] class OutgoingViewFull(StatsView): @@ -48,56 +76,46 @@ class OutgoingViewFull(StatsView): contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) - counter = 0 - - rr_list = [ - ("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."), - ("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."), - ("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."), - ("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."), - ] - - for outgoingType, label, image, tooltip in rr_list: + for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats: baseBox = wx.BoxSizer(wx.VERTICAL) baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER) - if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") - else: - lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") - + lbl = wx.StaticText(parent, wx.ID_ANY, valueFormat.format(0, "")) lbl.SetToolTip(wx.ToolTip(tooltip)) - - setattr(self, "label%s" % outgoingType, lbl) + setattr(self, labelName, lbl) baseBox.Add(lbl, 0, wx.ALIGN_CENTER) self._cachedValues.append(0) - counter += 1 sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT) def refreshPanel(self, fit): - # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), - ] + def formatTooltip(text, preSpool, fullSpool, prec, lowest, highest): + if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec): + return False, text + else: + return True, "{}\nSpool up: {}-{}".format( + text, + formatAmount(preSpool, prec, lowest, highest), + formatAmount(fullSpool, prec, lowest, highest)) + # TODO: fetch spoolup option + defaultSpoolValue = 1 counter = 0 - for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: + for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats: label = getattr(self, labelName) - value = value() if fit is not None else 0 - value = value if value is not None else 0 - if self._cachedValues[counter] != value: - valueStr = formatAmount(value, prec, lowest, highest) - label.SetLabel(valueFormat % valueStr) - tipStr = valueFormat % valueStr if altFormat is None else altFormat % value - label.SetToolTip(wx.ToolTip(tipStr)) - self._cachedValues[counter] = value + val = val(fit, defaultSpoolValue) if fit is not None else 0 + preSpoolVal = preSpoolVal(fit) if fit is not None else 0 + fullSpoolVal = fullSpoolVal(fit) if fit is not None else 0 + if self._cachedValues[counter] != val: + hasSpool, tooltipText = formatTooltip(tooltip, preSpoolVal, fullSpoolVal, prec, lowest, highest) + label.SetLabel(valueFormat.format( + formatAmount(val, prec, lowest, highest), + "\u02e2" if hasSpool else "")) + label.SetToolTip(wx.ToolTip(tooltipText)) + self._cachedValues[counter] = val counter += 1 self.panel.Layout() self.headerPanel.Layout() diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py index 21fe9bede..055a65e9e 100644 --- a/gui/builtinStatsViews/outgoingViewMinimal.py +++ b/gui/builtinStatsViews/outgoingViewMinimal.py @@ -20,7 +20,35 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.utils.numberFormatter import formatAmount +from gui.utils.numberFormatter import formatAmount, roundToPrec +from eos.utils.spoolSupport import SpoolType, SpoolOptions + + +stats = [ + ( + "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Capacitor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Capacitor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Capacitor", 0), + 3, 0, 0), + ( + "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Shield", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Shield", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Shield", 0), + 3, 0, 0), + ( + "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Armor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Armor", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Armor", 0), + 3, 0, 0), + ( + "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored", + lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Hull", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Hull", 0), + lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Hull", 0), + 3, 0, 0)] class OutgoingViewMinimal(StatsView): @@ -47,56 +75,46 @@ class OutgoingViewMinimal(StatsView): contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) - counter = 0 - - rr_list = [ - ("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."), - ("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."), - ("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."), - ("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."), - ] - - for outgoingType, label, image, tooltip in rr_list: + for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats: baseBox = wx.BoxSizer(wx.VERTICAL) - baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, label), 0, wx.ALIGN_CENTER) - - if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") - else: - lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") + baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, labelDesc), 0, wx.ALIGN_CENTER) + lbl = wx.StaticText(parent, wx.ID_ANY, valueFormat.format(0, "")) lbl.SetToolTip(wx.ToolTip(tooltip)) - - setattr(self, "label%s" % outgoingType, lbl) + setattr(self, labelName, lbl) baseBox.Add(lbl, 0, wx.ALIGN_CENTER) self._cachedValues.append(0) - counter += 1 sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT) def refreshPanel(self, fit): - # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), - ] + def formatTooltip(text, preSpool, fullSpool, prec, lowest, highest): + if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec): + return False, text + else: + return True, "{}\nSpool up: {}-{}".format( + text, + formatAmount(preSpool, prec, lowest, highest), + formatAmount(fullSpool, prec, lowest, highest)) + # TODO: fetch spoolup option + defaultSpoolValue = 1 counter = 0 - for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: + for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats: label = getattr(self, labelName) - value = value() if fit is not None else 0 - value = value if value is not None else 0 - if self._cachedValues[counter] != value: - valueStr = formatAmount(value, prec, lowest, highest) - label.SetLabel(valueFormat % valueStr) - tipStr = valueFormat % valueStr if altFormat is None else altFormat % value - label.SetToolTip(wx.ToolTip(tipStr)) - self._cachedValues[counter] = value + val = val(fit, defaultSpoolValue) if fit is not None else 0 + preSpoolVal = preSpoolVal(fit) if fit is not None else 0 + fullSpoolVal = fullSpoolVal(fit) if fit is not None else 0 + if self._cachedValues[counter] != val: + hasSpool, tooltipText = formatTooltip(tooltip, preSpoolVal, fullSpoolVal, prec, lowest, highest) + label.SetLabel(valueFormat.format( + formatAmount(val, prec, lowest, highest), + "\u02e2" if hasSpool else "")) + label.SetToolTip(wx.ToolTip(tooltipText)) + self._cachedValues[counter] = val counter += 1 self.panel.Layout() self.headerPanel.Layout() diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 9eb03ea22..767ad958f 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -129,15 +129,15 @@ class PriceViewFull(StatsView): total_price = 0 - if (self.settings.get("ship")): + if self.settings.get("ship"): total_price += ship_price - if (self.settings.get("modules")): + if self.settings.get("modules"): total_price += module_price - if (self.settings.get("drones")): + if self.settings.get("drones"): total_price += drone_price + fighter_price - if (self.settings.get("cargo")): + if self.settings.get("cargo"): total_price += cargo_price - if (self.settings.get("character")): + if self.settings.get("character"): total_price += booster_price + implant_price self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True)) diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py index ec1d5357f..8d6a5ce83 100644 --- a/gui/builtinStatsViews/priceViewMinimal.py +++ b/gui/builtinStatsViews/priceViewMinimal.py @@ -125,15 +125,15 @@ class PriceViewMinimal(StatsView): total_price = 0 - if (self.settings.get("ship")): + if self.settings.get("ship"): total_price += ship_price - if (self.settings.get("modules")): + if self.settings.get("modules"): total_price += module_price - if (self.settings.get("drones")): + if self.settings.get("drones"): total_price += drone_price + fighter_price - if (self.settings.get("cargo")): + if self.settings.get("cargo"): total_price += cargo_price - if (self.settings.get("character")): + if self.settings.get("character"): total_price += booster_price + implant_price self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True)) diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index eadcf537f..ccc89f79f 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -63,15 +63,20 @@ class RechargeViewFull(StatsView): # Add an empty label first for correct alignment. sizerTankStats.Add(wx.StaticText(contentPanel, wx.ID_ANY, ""), 0) - toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost", - "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"} + toolTipText = { + "shieldPassive": "Passive shield recharge", + "shieldActive": "Active shield boost", + "armorActive": "Armor repair amount", + "hullActive": "Hull repair amount"} for tankType in ("shieldPassive", "shieldActive", "armorActive", "hullActive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) bitmap.SetToolTip(tooltip) sizerTankStats.Add(bitmap, 0, wx.ALIGN_CENTER) - toolTipText = {"reinforced": "Reinforced", "sustained": "Sustained"} + toolTipText = { + "reinforced": "Reinforced", + "sustained": "Sustained"} for stability in ("reinforced", "sustained"): bitmap = BitmapLoader.getStaticBitmap("regen%s_big" % stability.capitalize(), contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[stability]) @@ -85,7 +90,6 @@ class RechargeViewFull(StatsView): tankTypeCap = tankType[0].capitalize() + tankType[1:] lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style=wx.ALIGN_RIGHT) setattr(self, "labelTank%s%s" % (stability.capitalize(), tankTypeCap), lbl) - box = wx.BoxSizer(wx.HORIZONTAL) box.Add(lbl, 0, wx.EXPAND) @@ -115,9 +119,23 @@ class RechargeViewFull(StatsView): unitlbl = getattr(self, "unitLabelTank%s%sActive" % (stability.capitalize(), name.capitalize())) unitlbl.SetLabel(unit) if tank is not None: - lbl.SetLabel("%.1f" % tank["%sRepair" % name]) + amount = tank["{}Repair".format(name)] else: - lbl.SetLabel("0.0") + amount = 0 + + if tank is not None and name == "armor": + preSpoolAmount = tank["armorRepairPreSpool"] + fullSpoolAmount = tank["armorRepairFullSpool"] + if round(preSpoolAmount, 1) != round(fullSpoolAmount, 1): + ttText = "Spool up: {:.1f}-{:.1f}".format(preSpoolAmount, fullSpoolAmount) + else: + ttText = "" + else: + ttText = "" + + lbl.SetLabel("{:.1f}{}".format(amount, "\u02e2" if ttText else "")) + lbl.SetToolTip(wx.ToolTip(ttText)) + unitlbl.SetToolTip(wx.ToolTip(ttText)) if fit is not None: label = getattr(self, "labelTankSustainedShieldPassive") diff --git a/gui/builtinViewColumns/ammoIcon.py b/gui/builtinViewColumns/ammoIcon.py index 7459db6a4..647d235a0 100644 --- a/gui/builtinViewColumns/ammoIcon.py +++ b/gui/builtinViewColumns/ammoIcon.py @@ -43,7 +43,7 @@ class AmmoIcon(ViewColumn): if stuff.charge is None: return -1 else: - iconFile = stuff.charge.icon.iconFile if stuff.charge.icon else "" + iconFile = stuff.charge.iconID if stuff.charge.iconID else "" if iconFile: return self.fittingView.imageList.GetImageIndex(iconFile, "icons") else: diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 7ad8743d7..b36cf7132 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -41,7 +41,7 @@ class AttributeDisplay(ViewColumn): iconFile = "pg_small" iconType = "gui" else: - iconFile = info.icon.iconFile if info.icon else None + iconFile = info.iconID iconType = "icons" if iconFile: self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index 8c09a9fa3..207d639c5 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -35,10 +35,10 @@ class BaseIcon(ViewColumn): return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui") else: - return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "") + return self.loadIconFile(stuff.item.iconID or "") item = getattr(stuff, "item", stuff) - return self.loadIconFile(item.icon.iconFile if item.icon else "") + return self.loadIconFile(item.iconID) def loadIconFile(self, iconFile): if iconFile: diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 8ed1466ac..368dd74b6 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -28,7 +28,9 @@ from eos.saveddata.fighter import Fighter from eos.saveddata.module import Module, Slot, Rack from eos.saveddata.fit import Fit from service.fit import Fit as FitSvc +from service.market import Market from gui.viewColumn import ViewColumn +from gui.builtinContextMenus.whProjector import WhProjector import gui.mainFrame pyfalog = Logger(__name__) @@ -73,10 +75,19 @@ class BaseName(ViewColumn): if stuff.slot == Slot.MODE: return '─ Tactical Mode ─' else: - return '─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize()) + return '─ {} {} Slot{}─'.format(stuff.num, Slot.getName(stuff.slot).capitalize(), '' if stuff.num == 1 else 's') else: return "" elif isinstance(stuff, Module): + if self.projectedView: + # check for projected abyssal name + name_check = stuff.item.name[0:-2] + type = WhProjector.abyssal_mapping.get(name_check, None) + if type: + sMkt = Market.getInstance() + type = sMkt.getItem(type) + return "{} {}".format(type.name, stuff.item.name[-1:]) + if stuff.isEmpty: return "%s Slot" % Slot.getName(stuff.slot).capitalize() else: diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index ef420c344..24f72b3a1 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -40,7 +40,7 @@ class MaxRange(ViewColumn): info = sAttr.getAttributeInfo("maxRange") self.info = info if params["showIcon"]: - iconFile = info.icon.iconFile if info.icon else None + iconFile = info.iconID if iconFile: self.imageId = fittingView.imageList.GetImageIndex(iconFile, "icons") self.bitmap = BitmapLoader.getBitmap(iconFile, "icons") @@ -60,7 +60,7 @@ class MaxRange(ViewColumn): maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange", None) falloff = stuff.falloff - if falloff: + if falloff and falloff >= 5: falloff = "+%sm" % formatAmount(falloff, 3, 0, 3) else: falloff = "" diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 668979c1f..e9bff3d56 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -27,7 +27,7 @@ from gui.viewColumn import ViewColumn from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from gui.utils.listFormatter import formatList -from eos.saveddata.drone import Drone +from eos.utils.spoolSupport import SpoolType, SpoolOptions class Miscellanea(ViewColumn): @@ -93,7 +93,9 @@ class Miscellanea(ViewColumn): text = "" tooltip = "" elif max(doomsday_duration / doomsday_dottime, 1) > 1: - text = "{0} dmg over {1} s".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3), doomsday_duration / 1000) + text = "{} over {}s".format( + formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 6), + formatAmount((doomsday_duration / 1000), 0, 0, 0)) tooltip = "Raw damage done over time" else: text = "{0} dmg".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3)) @@ -108,6 +110,25 @@ class Miscellanea(ViewColumn): text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) tooltip = "Tracking speed" return text, tooltip + elif itemGroup == "Precursor Weapon": + info = [] + trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") + if trackingSpeed: + text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) + tooltip = "tracking speed" + info.append((text, tooltip)) + # TODO: fetch spoolup option + defaultSpoolValue = 1 + spoolTime = stuff.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False))[1] + if spoolTime: + text = "{0}s".format(formatAmount(spoolTime, 3, 0, 3)) + tooltip = "spool up time" + info.append((text, tooltip)) + if not info: + return "", None + text = ' | '.join(i[0] for i in info) + tooltip = ' and '.join(i[1] for i in info).capitalize() + return text, tooltip elif itemCategory == "Subsystem": slots = ("hi", "med", "low") info = [] @@ -311,39 +332,61 @@ class Miscellanea(ViewColumn): tooltip = "Sensor recalibration time" return text, tooltip elif itemGroup == "Remote Armor Repairer": - repAmount = stuff.getModifiedItemAttr("armorDamageAmount") - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: + rps = stuff.getRemoteReps(ignoreState=True)[1] + if not rps: return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Armor repaired per second" return text, tooltip - elif itemGroup == "Remote Shield Booster": - repAmount = stuff.getModifiedItemAttr("shieldBonus") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: + elif itemGroup == "Mutadaptive Remote Armor Repairer": + # TODO: fetch spoolup option + defaultSpoolValue = 1 + spoolOptDefault = SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False) + spoolOptPre = SpoolOptions(SpoolType.SCALE, 0, True) + spoolOptFull = SpoolOptions(SpoolType.SCALE, 1, True) + rrType, rps = stuff.getRemoteReps(spoolOptions=spoolOptDefault, ignoreState=True) + rrTypePre, rpsPre = stuff.getRemoteReps(spoolOptions=spoolOptPre, ignoreState=True) + rrTypeFull, rpsFull = stuff.getRemoteReps(spoolOptions=spoolOptFull, ignoreState=True) + if not rps: return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + text = [] + tooltip = [] + text.append("{}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True))) + tooltip.append("Armor repaired per second") + spoolTime = stuff.getSpoolData(spoolOptDefault)[1] + if spoolTime: + text.append("{}s".format(formatAmount(spoolTime, 3, 0, 3))) + tooltip.append("spool up time") + text = " | ".join(text) + tooltip = " and ".join(tooltip) + spoolTimePre = stuff.getSpoolData(spoolOptPre)[1] + spoolTimeFull = stuff.getSpoolData(spoolOptFull)[1] + if spoolTimePre != spoolTimeFull: + tooltip = "{}\nSpool up: {}-{} over {}s".format( + tooltip, + formatAmount(rpsPre, 3, 0, 3), + formatAmount(rpsFull, 3, 0, 3), + formatAmount(spoolTimeFull - spoolTimePre, 3, 0, 3)) + return text, tooltip + elif itemGroup == "Remote Shield Booster": + rps = stuff.getRemoteReps(ignoreState=True)[1] + if not rps: + return "", None + text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Shield transferred per second" return text, tooltip elif itemGroup == "Remote Capacitor Transmitter": - repAmount = stuff.getModifiedItemAttr("powerTransferAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: + rps = stuff.getRemoteReps(ignoreState=True)[1] + if not rps: return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Energy transferred per second" return text, tooltip elif itemGroup == "Remote Hull Repairer": - repAmount = stuff.getModifiedItemAttr("structureDamageAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: + rps = stuff.getRemoteReps(ignoreState=True)[1] + if not rps: return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Structure repaired per second" return text, tooltip elif itemGroup == "Gang Coordinator": @@ -441,28 +484,11 @@ class Miscellanea(ViewColumn): tooltip = "Mining Yield per second ({0} per hour)".format(formatAmount(minePerSec * 3600, 3, 0, 3)) return text, tooltip elif itemGroup == "Logistic Drone": - armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") - shieldAmount = stuff.getModifiedItemAttr("shieldBonus") - hullAmount = stuff.getModifiedItemAttr("structureDamageAmount") - repAmount = armorAmount or shieldAmount or hullAmount - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: + repType, rps = stuff.getRemoteReps(ignoreState=True) + if not repType: return "", None - repPerSecPerDrone = repPerSec = float(repAmount) * 1000 / cycleTime - - if isinstance(stuff, Drone): - repPerSec *= stuff.amount - - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3)) - ttEntries = [] - if hullAmount is not None and repAmount == hullAmount: - ttEntries.append("structure") - if armorAmount is not None and repAmount == armorAmount: - ttEntries.append("armor") - if shieldAmount is not None and repAmount == shieldAmount: - ttEntries.append("shield") - - tooltip = "{0} HP repaired per second\n{1} HP/s per drone".format(formatList(ttEntries).capitalize(), repPerSecPerDrone) + text = "{}/s".format(formatAmount(rps, 3, 0, 3)) + tooltip = "{} HP repaired per second\n{} HP/s per drone".format(repType, formatAmount(rps / stuff.amount, 3, 0, 3)) return text, tooltip elif itemGroup == "Energy Neutralizer Drone": neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") @@ -478,7 +504,7 @@ class Miscellanea(ViewColumn): text = "{0}s".format(cycleTime) tooltip = "Spoolup time" return text, tooltip - elif itemGroup in ("Siege Module", "Cynosural Field"): + elif itemGroup in ("Siege Module", "Cynosural Field Generator"): amt = stuff.getModifiedItemAttr("consumptionQuantity") if amt: typeID = stuff.getModifiedItemAttr("consumptionType") diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 728376bef..56901c172 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -22,6 +22,7 @@ import wx from eos.saveddata.cargo import Cargo from eos.saveddata.drone import Drone +from eos.saveddata.price import PriceStatus from service.price import Price as ServicePrice from gui.viewColumn import ViewColumn from gui.bitmap_loader import BitmapLoader @@ -45,9 +46,15 @@ class Price(ViewColumn): if stuff.isEmpty: return "" - price = stuff.item.price.price + priceObj = stuff.item.price - if not price: + if not priceObj.isValid: + return False + + # Fetch actual price as float to not modify its value on Price object + price = priceObj.price + + if price == 0: return "" if isinstance(stuff, Drone) or isinstance(stuff, Cargo): @@ -59,11 +66,13 @@ class Price(ViewColumn): sPrice = ServicePrice.getInstance() def callback(item): - price = item.item.price - text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" - if price.failed: - text += " (!)" - colItem.SetText(text) + price = item[0] + textItems = [] + if price.price: + textItems.append(formatAmount(price.price, 3, 3, 9, currency=True)) + if price.status == PriceStatus.fail: + textItems.append("(!)") + colItem.SetText(" ".join(textItems)) display.SetItem(colItem) diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py index b4faa177a..abd91f730 100644 --- a/gui/builtinViewColumns/propertyDisplay.py +++ b/gui/builtinViewColumns/propertyDisplay.py @@ -41,7 +41,7 @@ class PropertyDisplay(ViewColumn): iconFile = "pg_small" iconType = "gui" else: - iconFile = info.icon.iconFile if info.icon else None + iconFile = info.iconID if info.icon else None iconType = "icons" if iconFile: self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 7e4eccb03..a0aea6043 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -21,27 +21,26 @@ import wx # noinspection PyPackageRequirements import wx.lib.newevent -import gui.mainFrame -from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED +from logbook import Logger + +import gui.builtinViews.emptyView import gui.display as d -from gui.contextMenu import ContextMenu -from gui.builtinShipBrowser.events import EVT_FIT_RENAMED, EVT_FIT_REMOVED, FitSelected, EVT_FIT_SELECTED +import gui.fitCommands as cmd +import gui.globalEvents as GE +import gui.mainFrame import gui.multiSwitch from eos.saveddata.mode import Mode -from eos.saveddata.module import Module, Slot, Rack -from gui.builtinViewColumns.state import State +from eos.saveddata.module import Module, Rack, Slot from gui.bitmap_loader import BitmapLoader -import gui.builtinViews.emptyView -from logbook import Logger +from gui.builtinMarketBrowser.events import ITEM_SELECTED +from gui.builtinShipBrowser.events import EVT_FIT_REMOVED, EVT_FIT_RENAMED, EVT_FIT_SELECTED, FitSelected +from gui.builtinViewColumns.state import State from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED - +from gui.contextMenu import ContextMenu +from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit from service.market import Market -from gui.utils.staticHelpers import DragDropHelper - -import gui.globalEvents as GE - pyfalog = Logger(__name__) @@ -88,7 +87,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner): def handleDrag(self, type, fitID): if type == "fit": - for page in self.multiSwitch.pages: + for page in self.multiSwitch._pages: if isinstance(page, FittingView) and page.activeFitID == fitID: index = self.multiSwitch.GetPageIndex(page) self.multiSwitch.SetSelection(index) @@ -148,6 +147,7 @@ class FittingView(d.Display): self.mainFrame.Bind(EVT_FIT_RENAMED, self.fitRenamed) self.mainFrame.Bind(EVT_FIT_REMOVED, self.fitRemoved) self.mainFrame.Bind(ITEM_SELECTED, self.appendItem) + self.font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) @@ -222,12 +222,15 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def Destroy(self): + # @todo: when wxPython 4.0.2 is release, https://github.com/pyfa-org/Pyfa/issues/1586#issuecomment-390074915 + # Make sure to remove the shitty checks that I have to put in place for these handlers to ignore when self is None print("+++++ Destroy " + repr(self)) - print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED)) - print(self.mainFrame.Unbind(GE.FIT_CHANGED)) - print(self.mainFrame.Unbind(EVT_FIT_RENAMED)) - print(self.mainFrame.Unbind(EVT_FIT_REMOVED)) - print(self.mainFrame.Unbind(ITEM_SELECTED)) + + # print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED)) + # print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged)) + # print(self.mainFrame.Unbind(EVT_FIT_RENAMED, handler=self.fitRenamed )) + # print(self.mainFrame.Unbind(EVT_FIT_REMOVED, handler=self.fitRemoved)) + # print(self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem)) d.Display.Destroy(self) @@ -260,7 +263,9 @@ class FittingView(d.Display): sel = [] row = self.GetFirstSelected() while row != -1: - sel.append(self.mods[self.GetItemData(row)]) + mod = self.mods[self.GetItemData(row)] + if mod and not isinstance(mod, Rack): + sel.append(mod) row = self.GetNextSelected(row) return sel @@ -291,6 +296,9 @@ class FittingView(d.Display): """ print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) pyfalog.debug("FittingView::fitRemoved") + if not self: + event.Skip() + return if event.fitID == self.getActiveFit(): pyfalog.debug(" Deleted fit is currently active") self.parent.DeletePage(self.parent.GetPageIndex(self)) @@ -311,6 +319,9 @@ class FittingView(d.Display): event.Skip() def fitRenamed(self, event): + if not self: + event.Skip() + return fitID = event.fitID if fitID == self.getActiveFit(): self.updateTab() @@ -347,12 +358,19 @@ class FittingView(d.Display): self.parent.SetPageTextIcon(pageIndex, text, bitmap) def appendItem(self, event): + """ + Adds items that are double clicks from the market browser. We handle both modules and ammo + """ + if not self: + event.Skip() + return if self.parent.IsActive(self): itemID = event.itemID fitID = self.activeFitID if fitID is not None: sFit = Fit.getInstance() if sFit.isAmmo(itemID): + # If we've selected ammo, then apply to the selected module(s) modules = [] sel = self.GetFirstSelected() while sel != -1 and sel not in self.blanks: @@ -362,17 +380,14 @@ class FittingView(d.Display): sel = self.GetNextSelected(sel) if len(modules) > 0: - sFit.setAmmo(fitID, itemID, modules) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, itemID, modules)) else: - populate = sFit.appendModule(fitID, itemID) - if populate is not None: - self.slotsChanged() - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID, action="modadd", typeID=itemID)) + self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID)) event.Skip() def removeItem(self, event): + """Double Left Click - remove module""" if event.CmdDown(): return row, _ = self.HitTest(event.Position) @@ -386,36 +401,22 @@ class FittingView(d.Display): def removeModule(self, modules): """Removes a list of modules from the fit""" - sFit = Fit.getInstance() - if not isinstance(modules, list): modules = [modules] - positions = [mod.modPosition for mod in modules] - result = sFit.removeModule(self.activeFitID, positions) + self.mainFrame.command.Submit(cmd.GuiModuleRemoveCommand(self.activeFitID, modules)) - if result is not None: - self.slotsChanged() - ids = {mod.item.ID for mod in modules} - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID, action="moddel", typeID=ids)) - - def addModule(self, x, y, srcIdx): - """Add a module from the market browser""" + def addModule(self, x, y, itemID): + """Add a module from the market browser (from dragging it)""" dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() mod = self.mods[dstRow] if not isinstance(mod, Module): # make sure we're not adding something to a T3D Mode return - moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].modPosition, srcIdx) - if moduleChanged is None: - # the new module doesn't fit in specified slot, try to simply append it - wx.PostEvent(self.mainFrame, ItemSelected(itemID=srcIdx)) - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=srcIdx)) + self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID, self.mods[dstRow].modPosition)) def swapCargo(self, x, y, srcIdx): """Swap a module from cargo to fitting window""" @@ -428,14 +429,11 @@ class FittingView(d.Display): if not isinstance(module, Module): return - sFit = Fit.getInstance() - fit = sFit.getFit(self.activeFitID) - typeID = fit.cargo[srcIdx].item.ID - - sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.modPosition, srcIdx, - mstate.CmdDown() and module.isEmpty) - - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=typeID)) + self.mainFrame.command.Submit(cmd.GuiCargoToModuleCommand( + self.mainFrame.getActiveFit(), + module.modPosition, + srcIdx, + mstate.CmdDown() and module.isEmpty)) def swapItems(self, x, y, srcIdx): """Swap two modules in fitting window""" @@ -443,15 +441,9 @@ class FittingView(d.Display): sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) - if mstate.CmdDown(): - clone = True - else: - clone = False - dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: - mod1 = fit.modules[srcIdx] mod2 = self.mods[dstRow] @@ -462,13 +454,11 @@ class FittingView(d.Display): if mod1.slot != mod2.slot: return - if getattr(mod2, "modPosition") is not None: - if clone and mod2.isEmpty: - sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition) - else: - sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition) + clone = mstate.CmdDown() and mod2.isEmpty - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + fitID = self.mainFrame.getActiveFit() + if getattr(mod2, "modPosition") is not None: + self.mainFrame.command.Submit(cmd.GuiModuleSwapOrCloneCommand(fitID, srcIdx, mod2.modPosition, clone)) else: pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown"))) @@ -508,7 +498,7 @@ class FittingView(d.Display): # second loop modifies self.mods, rewrites self.blanks to represent actual index of blanks for i, (x, slot) in enumerate(self.blanks): self.blanks[i] = x + i # modify blanks with actual index - self.mods.insert(x + i, Rack.buildRack(slot)) + self.mods.insert(x + i, Rack.buildRack(slot, sum(m.slot == slot for m in self.mods))) if fit.mode: # Modes are special snowflakes and need a little manual loving @@ -516,7 +506,7 @@ class FittingView(d.Display): # while also marking the mode header position in the Blanks list if sFit.serviceFittingOptions["rackSlots"]: self.blanks.append(len(self.mods)) - self.mods.append(Rack.buildRack(Slot.MODE)) + self.mods.append(Rack.buildRack(Slot.MODE, None)) self.mods.append(fit.mode) else: @@ -527,8 +517,10 @@ class FittingView(d.Display): self.populate(self.mods) def fitChanged(self, event): - print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) - + # print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) + if not self: + event.Skip() + return try: if self.activeFitID is not None and self.activeFitID == event.fitID: self.generateMods() @@ -622,18 +614,18 @@ class FittingView(d.Display): else: mods = self.getSelectedMods() - sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() ctrl = event.cmdDown or event.middleIsDown click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left" - sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click) + + self.mainFrame.command.Submit(cmd.GuiModuleStateChangeCommand( + fitID, self.mods[self.GetItemData(row)].modPosition, [mod.modPosition for mod in mods], click)) # update state tooltip tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)]) if tooltip: self.SetToolTip(tooltip) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) else: event.Skip() @@ -665,14 +657,27 @@ class FittingView(d.Display): slot = Slot.getValue(slotType) slotMap[slot] = fit.getSlotsFree(slot) < 0 - font = wx.Font(self.GetClassDefaultAttributes().font) - for i, mod in enumerate(self.mods): self.SetItemBackgroundColour(i, self.GetBackgroundColour()) # only consider changing color if we're dealing with a Module if type(mod) is Module: - if slotMap[mod.slot] or getattr(mod, 'restrictionOverridden', None): # Color too many modules as red + hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None) + # If module had broken fitting restrictions but now doesn't, + # ensure it is now valid, and remove restrictionOverridden + # variable. More in #1519 + if not fit.ignoreRestrictions and hasRestrictionOverriden: + clean = False + if mod.fits(fit, False): + if not mod.hardpoint: + clean = True + elif fit.getHardpointsFree(mod.hardpoint) >= 0: + clean = True + if clean: + del mod.restrictionOverridden + hasRestrictionOverriden = not hasRestrictionOverriden + + if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51)) elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled self.SetItemBackgroundColour(i, self.slotColour(mod.slot)) @@ -681,11 +686,11 @@ class FittingView(d.Display): if isinstance(mod, Rack) and \ sFit.serviceFittingOptions["rackSlots"] and \ sFit.serviceFittingOptions["rackLabels"]: - font.SetWeight(wx.FONTWEIGHT_BOLD) - self.SetItemFont(i, font) + self.font.SetWeight(wx.FONTWEIGHT_BOLD) + self.SetItemFont(i, self.font) else: - font.SetWeight(wx.FONTWEIGHT_NORMAL) - self.SetItemFont(i, font) + self.font.SetWeight(wx.FONTWEIGHT_NORMAL) + self.SetItemFont(i, self.font) self.Thaw() self.itemCount = self.GetItemCount() @@ -712,13 +717,12 @@ class FittingView(d.Display): # noinspection PyPropertyAccess def MakeSnapshot(self, maxColumns=1337): if self.FVsnapshot: - del self.FVsnapshot + self.FVsnapshot = None tbmp = wx.Bitmap(16, 16) tdc = wx.MemoryDC() tdc.SelectObject(tbmp) - font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) - tdc.SetFont(font) + tdc.SetFont(self.font) columnsWidths = [] for i in range(len(self.DEFAULT_COLS)): @@ -814,7 +818,7 @@ class FittingView(d.Display): mdc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))) mdc.Clear() - mdc.SetFont(font) + mdc.SetFont(self.font) mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) cx = padding diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index b391c1812..d33ff9779 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -156,7 +156,7 @@ class BaseImplantEditorView(wx.Panel): currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent)) items = sMkt.getItemsByMarketGroup(currentMktGrp) for item in items: - iconId = self.addMarketViewImage(item.icon.iconFile) + iconId = self.addMarketViewImage(item.iconID) tree.AppendItem(parent, item.name, iconId, data=item) tree.SortChildren(parent) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index c7a2fef8c..2b4d59341 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -17,35 +17,30 @@ # along with pyfa. If not, see . # ============================================================================= +import re + +import roman # noinspection PyPackageRequirements import wx import wx.dataview import wx.lib.agw.hyperlink - # noinspection PyPackageRequirements import wx.lib.newevent +from logbook import Logger # noinspection PyPackageRequirements from wx.dataview import TreeListCtrl -from gui.bitmap_loader import BitmapLoader -from gui.contextMenu import ContextMenu -import gui.globalEvents as GE -from gui.builtinViews.implantEditor import BaseImplantEditorView -from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog -from service.fit import Fit -from service.character import Character -from service.esi import Esi -from service.network import AuthenticationError, TimeoutError -from service.market import Market -from logbook import Logger - from wx.lib.agw.floatspin import FloatSpin - -from gui.utils.clipboard import toClipboard, fromClipboard - -import roman -import re -import webbrowser +import gui.globalEvents as GE +from gui.bitmap_loader import BitmapLoader +from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog +from gui.builtinViews.implantEditor import BaseImplantEditorView +from gui.contextMenu import ContextMenu +from gui.utils.clipboard import fromClipboard, toClipboard +from service.character import Character +from service.esi import Esi +from service.fit import Fit +from service.market import Market pyfalog = Logger(__name__) @@ -134,7 +129,12 @@ class CharacterEntityEditor(EntityEditor): def DoRename(self, entity, name): sChar = Character.getInstance() - sChar.rename(entity, name) + + if entity.alphaCloneID: + trimmed_name = re.sub('[ \(\u03B1\)]+$', '', name) + sChar.rename(entity, trimmed_name) + else: + sChar.rename(entity, name) def DoCopy(self, entity, name): sChar = Character.getInstance() @@ -734,17 +734,11 @@ class APIView(wx.Panel): self.stDisabledTip.Wrap(-1) hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10) - self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, - "You haven't logging into EVE SSO with any characters yet. Please use the " - "button below to log into EVE.", style=wx.ALIGN_CENTER) - self.noCharactersTip.Wrap(-1) - hintSizer.Add(self.noCharactersTip, 0, wx.TOP | wx.BOTTOM, 10) - self.stDisabledTip.Hide() hintSizer.AddStretchSpacer() pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5) - fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0) + fgSizerInput = wx.FlexGridSizer(1, 3, 0, 0) fgSizerInput.AddGrowableCol(1) fgSizerInput.SetFlexibleDirection(wx.BOTH) fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) @@ -754,15 +748,28 @@ class APIView(wx.Panel): fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10) self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) - fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 10) + fgSizerInput.Add(self.charChoice, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, 10) + + self.fetchButton = wx.Button(self, wx.ID_ANY, "Get Skills", wx.DefaultPosition, wx.DefaultSize, 0) + self.fetchButton.Bind(wx.EVT_BUTTON, self.fetchSkills) + fgSizerInput.Add(self.fetchButton, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10) pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5) + pmainSizer.AddStretchSpacer() + + self.m_staticline1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + pmainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 10) + + self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, "Don't see your EVE character in the list?", style=wx.ALIGN_CENTER) + + self.noCharactersTip.Wrap(-1) + pmainSizer.Add(self.noCharactersTip, 0, wx.CENTER | wx.TOP | wx.BOTTOM, 0) + self.addButton = wx.Button(self, wx.ID_ANY, "Log In with EVE SSO", wx.DefaultPosition, wx.DefaultSize, 0) self.addButton.Bind(wx.EVT_BUTTON, self.addCharacter) - pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 5) - self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) - pmainSizer.Add(self.stStatus, 0, wx.ALL, 5) + pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 10) + self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGOUT, self.ssoListChanged) self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoListChanged) self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) @@ -776,9 +783,18 @@ class APIView(wx.Panel): def ssoCharChanged(self, event): sChar = Character.getInstance() activeChar = self.charEditor.entityEditor.getActiveEntity() - sChar.setSsoCharacter(activeChar.ID, self.getActiveCharacter()) + ssoChar = self.getActiveCharacter() + sChar.setSsoCharacter(activeChar.ID, ssoChar) + + self.fetchButton.Enable(ssoChar is not None) + event.Skip() + def fetchSkills(self, evt): + sChar = Character.getInstance() + char = self.charEditor.entityEditor.getActiveEntity() + sChar.apiFetch(char.ID, self.__fetchCallback) + def addCharacter(self, event): sEsi = Esi.getInstance() sEsi.login() @@ -788,17 +804,8 @@ class APIView(wx.Panel): return self.charChoice.GetClientData(selection) if selection is not -1 else None def ssoListChanged(self, event): - sEsi = Esi.getInstance() - ssoChars = sEsi.getSsoCharacters() - - if len(ssoChars) == 0: - self.charChoice.Hide() - self.m_staticCharText.Hide() - self.noCharactersTip.Show() - else: - self.noCharactersTip.Hide() - self.m_staticCharText.Show() - self.charChoice.Show() + if not self: # todo: fix event not unbinding properly + return self.charChanged(event) @@ -814,6 +821,8 @@ class APIView(wx.Panel): sso = sChar.getSsoCharacter(activeChar.ID) + self.fetchButton.Enable(sso is not None) + ssoChars = sEsi.getSsoCharacters() self.charChoice.Clear() @@ -825,9 +834,9 @@ class APIView(wx.Panel): if sso is not None and char.ID == sso.ID: self.charChoice.SetSelection(currId) - if sso is None: - self.charChoice.SetSelection(noneID) + if sso is None: + self.charChoice.SetSelection(noneID) # # if chars: @@ -851,13 +860,17 @@ class APIView(wx.Panel): event.Skip() def __fetchCallback(self, e=None): - charName = self.charChoice.GetString(self.charChoice.GetSelection()) - if e is None: - self.stStatus.SetLabel("Successfully fetched {}\'s skills from EVE API.".format(charName)) - else: + if e: exc_type, exc_obj, exc_trace = e - pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}".format(charName, exc_obj)) - self.stStatus.SetLabel("Unable to retrieve {}\'s skills. Error message:\n{}".format(charName, exc_obj)) + pyfalog.warn("Error fetching skill information for character") + pyfalog.warn(exc_obj) + + wx.MessageBox( + "Error fetching skill information", + "Error", wx.ICON_ERROR | wx.STAY_ON_TOP) + else: + wx.MessageBox( + "Successfully fetched skills", "Success", wx.ICON_INFORMATION | wx.STAY_ON_TOP) class SecStatusDialog(wx.Dialog): diff --git a/gui/characterSelection.py b/gui/characterSelection.py index cc63a1f82..051ce69b4 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -170,7 +170,6 @@ class CharacterSelection(wx.Panel): def charChanged(self, event): fitID = self.mainFrame.getActiveFit() charID = self.getActiveCharacter() - sChar = Character.getInstance() if charID == -1: # revert to previous character diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index c03edacab..66c1c262e 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # # ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is # selected, most likely due to determination of mouse position @@ -11,7 +11,7 @@ # tab index?). This will also help with finding close buttons. # ToDo: Fix page preview code (PFNotebookPagePreview) # -#= ============================================================================== +# =============================================================================== import wx import wx.lib.newevent @@ -20,6 +20,7 @@ from gui.bitmap_loader import BitmapLoader from gui.utils import draw from gui.utils import color as color_utils from service.fit import Fit +from gui.utils import fonts _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -29,7 +30,7 @@ PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() -class VetoAble(): +class VetoAble: def __init__(self): self.__vetoed = False @@ -40,7 +41,7 @@ class VetoAble(): return self.__vetoed -class NotebookTabChangeEvent(): +class NotebookTabChangeEvent: def __init__(self, old, new): self.__old = old self.__new = new @@ -357,7 +358,7 @@ class _TabRenderer: self.tab_bitmap = None self.tab_back_bitmap = None self.padding = 4 - self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) self.tab_img = img self.position = (0, 0) # Not used internally for rendering - helper for tab container @@ -413,7 +414,7 @@ class _TabRenderer: mdc.SelectObject(ebmp) mdc.SetFont(self.font) textSizeX, textSizeY = mdc.GetTextExtent(self.text) - totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2 + totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding * 2 mdc.SelectObject(wx.NullBitmap) return totalSize, self.tab_height @@ -551,24 +552,6 @@ class _TabRenderer: bmp, self.left_width + self.padding - bmp.GetWidth() / 2, (height - bmp.GetHeight()) / 2) - text_start = self.left_width + self.padding + bmp.GetWidth() / 2 - else: - text_start = self.left_width - - mdc.SetFont(self.font) - - maxsize = self.tab_width \ - - text_start \ - - self.right_width \ - - self.padding * 4 - color = self.selected_color if self.selected else self.inactive_color - - mdc.SetTextForeground(color_utils.GetSuitable(color, 1)) - - # draw text (with no ellipses) - text = draw.GetPartialText(mdc, self.text, maxsize, "") - tx, ty = mdc.GetTextExtent(text) - mdc.DrawText(text, text_start + self.padding, height / 2 - ty / 2) # draw close button if self.closeable: @@ -595,6 +578,30 @@ class _TabRenderer: bmp = wx.Bitmap(img) self.tab_bitmap = bmp + # We draw the text separately in order to draw it directly on the native DC, rather than a memory one, because + # drawing text on a memory DC draws it blurry on HD/Retina screens + def DrawText(self, dc): + height = self.tab_height + dc.SetFont(self.font) + + if self.tab_img: + text_start = self.left_width + self.padding + self.tab_img.GetWidth() / 2 + else: + text_start = self.left_width + + maxsize = self.tab_width \ + - text_start \ + - self.right_width \ + - self.padding * 4 + color = self.selected_color if self.selected else self.inactive_color + + dc.SetTextForeground(color_utils.GetSuitable(color, 1)) + + # draw text (with no ellipses) + text = draw.GetPartialText(dc, self.text, maxsize, "") + tx, ty = dc.GetTextExtent(text) + dc.DrawText(text, text_start + self.padding, height / 2 - ty / 2) + def __repr__(self): return "_TabRenderer(text={}, disabled={}) at {}".format( self.text, self.disabled, hex(id(self)) @@ -729,6 +736,7 @@ class _TabsContainer(wx.Panel): self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp) self.Bind(wx.EVT_MOTION, self.OnMotion) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) @@ -769,6 +777,29 @@ class _TabsContainer(wx.Panel): self.dragged_tab = tab + def OnMiddleUp(self, event): + mposx, mposy = event.GetPosition() + + tab = self.FindTabAtPos(mposx, mposy) + + if tab is None or not tab.closeable: # if not able to close, return False + return False + + index = self.tabs.index(tab) + ev = PageClosing(index) + wx.PostEvent(self.Parent, ev) + + if ev.isVetoed(): + return False + + index = self.GetTabIndex(tab) + self.Parent.DeletePage(index) + wx.PostEvent(self.Parent, PageClosed(index=index)) + + sel = self.GetSelected() + if sel is not None: + wx.PostEvent(self.Parent, PageChanged(-1, sel)) + def OnMotion(self, event): """ Determines what happens when the mouse moves. This handles primarily @@ -1144,6 +1175,10 @@ class _TabsContainer(wx.Panel): img = img.AdjustChannels(1, 1, 1, 0.85) bmp = wx.Bitmap(img) mdc.DrawBitmap(bmp, posx, posy, True) + + mdc.SetDeviceOrigin(posx, posy) + tab.DrawText(mdc) + mdc.SetDeviceOrigin(0, 0) else: selected = tab @@ -1163,6 +1198,10 @@ class _TabsContainer(wx.Panel): mdc.DrawBitmap(bmp, posx, posy, True) + mdc.SetDeviceOrigin(posx, posy) + selected.DrawText(mdc) + mdc.SetDeviceOrigin(0, 0) + def OnErase(self, event): pass @@ -1322,7 +1361,7 @@ class PFNotebookPagePreview(wx.Frame): self.padding = 15 self.transp = 0 - hfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + hfont = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) self.SetFont(hfont) tx, ty = self.GetTextExtent(self.title) @@ -1384,7 +1423,7 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + font = wx.Font(11, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) mdc.SetFont(font) x, y = mdc.GetTextExtent(self.title) @@ -1478,4 +1517,3 @@ if __name__ == "__main__": top = Frame("Test Chrome Tabs") top.Show() app.MainLoop() - diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 11d574157..7c1caf085 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -186,8 +186,9 @@ from gui.builtinContextMenus import ( # noqa: E402,F401 itemStats, damagePattern, marketJump, - droneSplit, + # droneSplit, itemRemove, + fillWithModule, droneRemoveStack, ammoPattern, project, @@ -202,11 +203,12 @@ from gui.builtinContextMenus import ( # noqa: E402,F401 priceOptions, amount, cargoAmmo, - droneStack, + # droneStack, metaSwap, implantSets, fighterAbilities, boosterSideEffects, commandFits, - tabbedFits + tabbedFits, + mutaplasmids, ) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 5675c835e..91725e048 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -18,51 +18,104 @@ # ============================================================================= +from collections import OrderedDict + # noinspection PyPackageRequirements import wx +from service.port.eft import EFT_OPTIONS +from service.port.multibuy import MULTIBUY_OPTIONS +from service.settings import SettingsProvider + class CopySelectDialog(wx.Dialog): copyFormatEft = 0 - copyFormatEftImps = 1 - copyFormatXml = 2 - copyFormatDna = 3 - copyFormatEsi = 4 - copyFormatMultiBuy = 5 + copyFormatXml = 1 + copyFormatDna = 2 + copyFormatEsi = 3 + copyFormatMultiBuy = 4 + copyFormatEfs = 5 def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) - copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "CREST", "MultiBuy"] - copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format", - CopySelectDialog.copyFormatEftImps: "EFT text format", - CopySelectDialog.copyFormatXml: "EVE native XML format", - CopySelectDialog.copyFormatDna: "A one-line text format", - CopySelectDialog.copyFormatEsi: "A JSON format used for EVE CREST", - CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format"} - selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats, - style=wx.RA_SPECIFY_ROWS) - selector.Bind(wx.EVT_RADIOBOX, self.Selected) - for format, tooltip in copyFormatTooltips.items(): - selector.SetItemToolTip(format, tooltip) + self.copyFormats = OrderedDict(( + ("EFT", (CopySelectDialog.copyFormatEft, EFT_OPTIONS)), + ("MultiBuy", (CopySelectDialog.copyFormatMultiBuy, MULTIBUY_OPTIONS)), + ("ESI", (CopySelectDialog.copyFormatEsi, None)), + ("EFS", (CopySelectDialog.copyFormatEfs, None)), + # ("XML", (CopySelectDialog.copyFormatXml, None)), + # ("DNA", (CopySelectDialog.copyFormatDna, None)), + )) - self.copyFormat = CopySelectDialog.copyFormatEft - selector.SetSelection(self.copyFormat) + defaultFormatOptions = {} + for formatId, formatOptions in self.copyFormats.values(): + if formatOptions is None: + continue + defaultFormatOptions[formatId] = {opt[0]: opt[3] for opt in formatOptions} - mainSizer.Add(selector, 0, wx.EXPAND | wx.ALL, 5) + self.settings = SettingsProvider.getInstance().getSettings("pyfaExport", {"format": 0, "options": defaultFormatOptions}) + # Options used to be stored as int (EFT export options only), + # overwrite them with new format when needed + if isinstance(self.settings["options"], int): + self.settings["options"] = defaultFormatOptions + + self.options = {} + + initialized = False + for formatName, formatData in self.copyFormats.items(): + formatId, formatOptions = formatData + if not initialized: + rdo = wx.RadioButton(self, wx.ID_ANY, formatName, style=wx.RB_GROUP) + initialized = True + else: + rdo = wx.RadioButton(self, wx.ID_ANY, formatName) + rdo.Bind(wx.EVT_RADIOBUTTON, self.Selected) + if self.settings['format'] == formatId: + rdo.SetValue(True) + self.copyFormat = formatId + mainSizer.Add(rdo, 0, wx.EXPAND | wx.ALL, 5) + + if formatOptions: + bsizer = wx.BoxSizer(wx.VERTICAL) + self.options[formatId] = {} + + for optId, optName, optDesc, _ in formatOptions: + checkbox = wx.CheckBox(self, -1, optName) + self.options[formatId][optId] = checkbox + if self.settings['options'].get(formatId, {}).get(optId, defaultFormatOptions.get(formatId, {}).get(optId)): + checkbox.SetValue(True) + bsizer.Add(checkbox, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 3) + mainSizer.Add(bsizer, 1, wx.EXPAND | wx.LEFT, 20) buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) if buttonSizer: mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 5) + self.toggleOptions() self.SetSizer(mainSizer) self.Fit() self.Center() def Selected(self, event): - self.copyFormat = event.GetSelection() + obj = event.GetEventObject() + formatName = obj.GetLabel() + self.copyFormat = self.copyFormats[formatName][0] + self.toggleOptions() + self.Fit() + + def toggleOptions(self): + for formatId in self.options: + for checkbox in self.options[formatId].values(): + checkbox.Enable(self.GetSelected() == formatId) def GetSelected(self): return self.copyFormat + + def GetOptions(self): + options = {} + for formatId in self.options: + options[formatId] = {optId: ch.IsChecked() for optId, ch in self.options[formatId].items()} + return options diff --git a/gui/devTools.py b/gui/devTools.py index 98a14662c..44012128c 100644 --- a/gui/devTools.py +++ b/gui/devTools.py @@ -35,7 +35,7 @@ class DevTools(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240)) - + self.mainFrame = parent self.block = False self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) @@ -56,13 +56,19 @@ class DevTools(wx.Dialog): self.fitTest = wx.Button(self, wx.ID_ANY, "Test fits", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.fitTest, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.fitTest .Bind(wx.EVT_BUTTON, self.fit_test) + self.fitTest.Bind(wx.EVT_BUTTON, self.fit_test) + + self.cmdPrint = wx.Button(self, wx.ID_ANY, "Command Print", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cmdPrint, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.cmdPrint.Bind(wx.EVT_BUTTON, self.cmd_print) self.SetSizer(mainSizer) self.Layout() self.CenterOnParent() self.Show() + print(parent) def objects_by_id(self, evt): input = self.id_get.GetValue() @@ -81,6 +87,11 @@ class DevTools(wx.Dialog): else: print(None) + def cmd_print(self, evt): + print("=" * 20) + for x in self.mainFrame.command.GetCommands(): + print("{}{} {}".format("==> " if x == self.mainFrame.command.GetCurrentCommand() else "", x.GetName(), x)) + def gc_collect(self, evt): print(gc.collect()) print(gc.get_debug()) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 706e3c5a3..d9112d4b8 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -26,6 +26,7 @@ import traceback import config from logbook import Logger from service.prereqsCheck import version_block +import datetime pyfalog = Logger(__name__) @@ -63,6 +64,11 @@ class ErrorFrame(wx.Frame): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) + from eos.config import gamedata_version, gamedata_date + + time = datetime.datetime.fromtimestamp(int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S') + version = "pyfa v" + config.getVersion() + '\nEVE Data Version: {} ({})\n\n'.format(gamedata_version, time) # gui.aboutData.versionString + desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \ "information about how this was triggered. Please contact the developers with the\n" \ "information provided through the EVE Online forums or file a GitHub issue." @@ -97,7 +103,7 @@ class ErrorFrame(wx.Frame): # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) - self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition, + self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version + version_block.strip(), wx.DefaultPosition, (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 92fd89b83..9d14ad8fe 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -1,5 +1,3 @@ -import time -import webbrowser import json # noinspection PyPackageRequirements import wx @@ -15,10 +13,9 @@ from gui.display import Display import gui.globalEvents as GE from logbook import Logger -import calendar from service.esi import Esi -from esipy.exceptions import APIException -from service.port import ESIExportException +from service.esiAccess import APIException +from service.port.esi import ESIExportException pyfalog = Logger(__name__) @@ -32,7 +29,6 @@ class EveFittings(wx.Frame): self.mainFrame = parent mainSizer = wx.BoxSizer(wx.VERTICAL) - sEsi = Esi.getInstance() characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -111,21 +107,23 @@ class EveFittings(wx.Frame): waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) try: - fittings = sEsi.getFittings(self.getActiveCharacter()) + self.fittings = sEsi.getFittings(self.getActiveCharacter()) # self.cacheTime = fittings.get('cached_until') # self.updateCacheStatus(None) # self.cacheTimer.Start(1000) - self.fitTree.populateSkillTree(fittings) + self.fitTree.populateSkillTree(self.fittings) del waitDialog except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) self.statusbar.SetStatusText(msg) except APIException as ex: - del waitDialog # Can't do this in a finally because then it obscures the message dialog + # Can't do this in a finally because then it obscures the message dialog + del waitDialog # noqa: F821 ESIExceptionHandler(self, ex) except Exception as ex: - del waitDialog + del waitDialog # noqa: F821 + raise ex def importFitting(self, event): selection = self.fitView.fitSelection @@ -133,7 +131,7 @@ class EveFittings(wx.Frame): return data = self.fitTree.fittingsTreeCtrl.GetItemData(selection) sPort = Port.getInstance() - fits = sPort.importFitFromBuffer(data) + import_type, fits = sPort.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): @@ -150,15 +148,32 @@ class EveFittings(wx.Frame): if dlg.ShowModal() == wx.ID_YES: try: sEsi.delFitting(self.getActiveCharacter(), data['fitting_id']) + # repopulate the fitting list + self.fitTree.populateSkillTree(self.fittings) + self.fitView.update([]) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) self.statusbar.SetStatusText(msg) -class ESIExceptionHandler(object): +class ESIServerExceptionHandler(object): def __init__(self, parentWindow, ex): - if ex.response['error'] == "invalid_token": + dlg = wx.MessageDialog(parentWindow, + "There was an issue starting up the localized server, try setting " + "Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> " + "Login Authentication Method. If this doesn't fix the problem please file an " + "issue on Github.", + "Add Character Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + pyfalog.error(ex) + + +class ESIExceptionHandler(object): + # todo: make this a generate excetpion handler for all calls + def __init__(self, parentWindow, ex): + if ex.response['error'].startswith('Token is not valid') or ex.response['error'] == 'invalid_token': # todo: this seems messy, figure out a better response dlg = wx.MessageDialog(parentWindow, "There was an error validating characters' SSO token. Please try " "logging into the character again to reset the token.", "Invalid Token", @@ -178,7 +193,6 @@ class ExportToEve(wx.Frame): self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - sEsi = Esi.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -242,29 +256,30 @@ class ExportToEve(wx.Frame): self.statusbar.SetStatusText("Sending request and awaiting response", 1) sEsi = Esi.getInstance() - try: - sFit = Fit.getInstance() - data = sPort.exportESI(sFit.getFit(fitID)) - res = sEsi.postFitting(self.getActiveCharacter(), data) + sFit = Fit.getInstance() + data = sPort.exportESI(sFit.getFit(fitID)) + res = sEsi.postFitting(self.getActiveCharacter(), data) + try: + res.raise_for_status() self.statusbar.SetStatusText("", 0) - self.statusbar.SetStatusText("", 1) - # try: - # text = json.loads(res.text) - # self.statusbar.SetStatusText(text['message'], 1) - # except ValueError: - # pyfalog.warning("Value error on loading JSON.") - # self.statusbar.SetStatusText("", 1) + self.statusbar.SetStatusText(res.reason, 1) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) - self.statusbar.SetStatusText(msg) + self.statusbar.SetStatusText("ERROR", 0) + self.statusbar.SetStatusText(msg, 1) except ESIExportException as ex: pyfalog.error(ex) self.statusbar.SetStatusText("ERROR", 0) - self.statusbar.SetStatusText(ex.args[0], 1) + self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1) except APIException as ex: - ESIExceptionHandler(self, ex) + try: + ESIExceptionHandler(self, ex) + except Exception as ex: + self.statusbar.SetStatusText("ERROR", 0) + self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1) + pyfalog.error(ex) class SsoCharacterMgmt(wx.Dialog): @@ -304,8 +319,8 @@ class SsoCharacterMgmt(wx.Dialog): self.Centre(wx.BOTH) def ssoLogin(self, event): - if (self): - #todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this. + if self: + # todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this. self.popCharList() event.Skip() @@ -323,10 +338,12 @@ class SsoCharacterMgmt(wx.Dialog): self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) - @staticmethod - def addChar(event): - sEsi = Esi.getInstance() - sEsi.login() + def addChar(self, event): + try: + sEsi = Esi.getInstance() + sEsi.login() + except Exception as ex: + ESIServerExceptionHandler(self, ex) def delChar(self, event): item = self.lcCharacters.GetFirstSelected() @@ -362,10 +379,17 @@ class FittingsTreeView(wx.Panel): tree = self.fittingsTreeCtrl tree.DeleteChildren(root) + sEsi = Esi.getInstance() + dict = {} fits = data for fit in fits: + if fit['fitting_id'] in sEsi.fittings_deleted: + continue ship = getItem(fit['ship_type_id']) + if ship is None: + pyfalog.debug('Cannot find ship type id: {}'.format(fit['ship_type_id'])) + continue if ship.name not in dict: dict[ship.name] = [] dict[ship.name].append(fit) diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py new file mode 100644 index 000000000..42b718c5d --- /dev/null +++ b/gui/fitCommands/__init__.py @@ -0,0 +1,37 @@ +from .guiToggleModuleState import GuiModuleStateChangeCommand +from .guiAddModule import GuiModuleAddCommand +from .guiRemoveModule import GuiModuleRemoveCommand +from .guiAddCharge import GuiModuleAddChargeCommand +from .guiFillWithModule import GuiFillWithModuleCommand +from .guiSwapCloneModule import GuiModuleSwapOrCloneCommand +from .guiRemoveCargo import GuiRemoveCargoCommand +from .guiAddCargo import GuiAddCargoCommand +from .guiRemoveImplant import GuiRemoveImplantCommand +from .guiAddImplant import GuiAddImplantCommand +from .guiAddBooster import GuiAddBoosterCommand +from .guiRemoveBooster import GuiRemoveBoosterCommand +from .guiAddCommand import GuiAddCommandCommand +from .guiRemoveCommand import GuiRemoveCommandCommand +from .guiSetMode import GuiSetModeCommand +from .guiToggleCommand import GuiToggleCommandCommand +from .guiAddProjected import GuiAddProjectedCommand +from .guiRemoveProjected import GuiRemoveProjectedCommand +from .guiCargoToModule import GuiCargoToModuleCommand +from .guiModuleToCargo import GuiModuleToCargoCommand +from .guiAddFighter import GuiAddFighterCommand +from .guiRemoveFighter import GuiRemoveFighterCommand +from .guiMetaSwap import GuiMetaSwapCommand +from .guiToggleFighter import GuiToggleFighterCommand +from .guiToggleImplant import GuiToggleImplantCommand +from .guiToggleBooster import GuiToggleBoosterCommand +from .guiAddDrone import GuiAddDroneCommand +from .guiRemoveDrone import GuiRemoveDroneCommand +from .guiChangeFighterQty import GuiChangeFighterQty +from .guiChangeCargoQty import GuiChangeCargoQty +from .guiChangeProjectedFitQty import GuiChangeProjectedFitQty +from .guiChangeDroneQty import GuiChangeDroneQty +from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty +from .guiToggleDrone import GuiToggleDroneCommand +from .guiFitRename import GuiFitRenameCommand +from .guiChangeImplantLocation import GuiChangeImplantLocation +from .guiImportMutatedModule import GuiImportMutatedModuleCommand diff --git a/gui/fitCommands/calc/__init__.py b/gui/fitCommands/calc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/gui/fitCommands/calc/fitAddBooster.py b/gui/fitCommands/calc/fitAddBooster.py new file mode 100644 index 000000000..0470b556b --- /dev/null +++ b/gui/fitCommands/calc/fitAddBooster.py @@ -0,0 +1,49 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.booster import Booster +pyfalog = Logger(__name__) + + +class FitAddBoosterCommand(wx.Command): + """" + from sFit.addBooster + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.new_index = None + self.old_item = None + + def Do(self): + pyfalog.debug("Adding booster ({0}) to fit ID: {1}", self.itemID, self.fitID) + + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager="attributes") + + if next((x for x in fit.boosters if x.itemID == self.itemID), None): + return False # already have item in list of boosters + + try: + booster = Booster(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", self.itemID) + return False + + self.old_item = fit.boosters.makeRoom(booster) + fit.boosters.append(booster) + self.new_index = fit.boosters.index(booster) + return True + + def Undo(self): + if self.old_item: + # If we had an item in the slot previously, add it back. + cmd = FitAddBoosterCommand(self.fitID, self.old_item) + cmd.Do() + return True + + from .fitRemoveBooster import FitRemoveBoosterCommand # Avoid circular import + cmd = FitRemoveBoosterCommand(self.fitID, self.new_index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddCargo.py b/gui/fitCommands/calc/fitAddCargo.py new file mode 100644 index 000000000..fff3abd26 --- /dev/null +++ b/gui/fitCommands/calc/fitAddCargo.py @@ -0,0 +1,43 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.cargo import Cargo +pyfalog = Logger(__name__) + + +class FitAddCargoCommand(wx.Command): + """" + from sFit.addCargo + """ + def __init__(self, fitID, itemID, amount=1, replace=False): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.itemID = itemID + self.amount = amount # add x amount. If this goes over amount, removes stack + self.replace = replace # if this is false, we increment. + + def Do(self): + pyfalog.debug("Adding cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID) + + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID) + + cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None) + + if cargo is None: + cargo = Cargo(item) + fit.cargo.append(cargo) + + if self.replace: + cargo.amount = self.amount + else: + cargo.amount += self.amount + + eos.db.commit() + return True + + def Undo(self): + from .fitRemoveCargo import FitRemoveCargoCommand # Avoid circular import + cmd = FitRemoveCargoCommand(self.fitID, self.itemID, self.amount) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddCommand.py b/gui/fitCommands/calc/fitAddCommand.py new file mode 100644 index 000000000..660d7b5a0 --- /dev/null +++ b/gui/fitCommands/calc/fitAddCommand.py @@ -0,0 +1,47 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitAddCommandCommand(wx.Command): # well that's an unfrtunate name + """" + from sFit.addCommand + """ + def __init__(self, fitID, commandFitID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.commandFitID = commandFitID + + def Do(self): + pyfalog.debug("Projecting command fit ({0}) onto: {1}", self.fitID, self.commandFitID) + fit = eos.db.getFit(self.fitID) + command = eos.db.getFit(self.commandFitID) + + if not command: + # if redoing when the command fit has been deleted, simply fail this command + return False + + if command in fit.commandFits: + return + + fit.commandFitDict[command.ID] = command + + # this bit is required -- see GH issue # 83 + eos.db.saveddata_session.flush() + eos.db.saveddata_session.refresh(command) + + eos.db.commit() + return True + + def Undo(self): + command = eos.db.getFit(self.commandFitID) + + if not command: + # can't find the command fit, it must have been deleted. Just skip this undo + return True + + from .fitRemoveCommand import FitRemoveCommandCommand + cmd = FitRemoveCommandCommand(self.fitID, self.commandFitID) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddDrone.py b/gui/fitCommands/calc/fitAddDrone.py new file mode 100644 index 000000000..a7b8ed41c --- /dev/null +++ b/gui/fitCommands/calc/fitAddDrone.py @@ -0,0 +1,49 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.drone import Drone +pyfalog = Logger(__name__) + + +class FitAddDroneCommand(wx.Command): + """" + from sFit.addDrone + """ + def __init__(self, fitID, itemID, amount=1, replace=False): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.itemID = itemID + self.amount = amount # add x amount. If this goes over amount, removes stack + self.replace = replace # if this is false, we increment. + self.index = None + + def Do(self): + pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", self.amount, self.itemID, self.fitID) + + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager=("attributes", "group.category")) + + for d in fit.drones.find(item): + if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]): + drone = d + break + else: + try: + drone = Drone(item) + except ValueError: + pyfalog.warning("Invalid drone: {}", item) + return False + + if not drone.fits(fit): + return False + fit.drones.append(drone) + + drone.amount += self.amount + eos.db.commit() + self.index = fit.drones.index(drone) + return True + + def Undo(self): + from .fitRemoveDrone import FitRemoveDroneCommand # Avoid circular import + cmd = FitRemoveDroneCommand(self.fitID, self.index, self.amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitAddFighter.py b/gui/fitCommands/calc/fitAddFighter.py new file mode 100644 index 000000000..42ca59be1 --- /dev/null +++ b/gui/fitCommands/calc/fitAddFighter.py @@ -0,0 +1,48 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.fighter import Fighter +pyfalog = Logger(__name__) + + +class FitAddFighterCommand(wx.Command): + """" + from sFit.addFighter + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.itemID = itemID + self.new_index = None + + def Do(self): + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager=("attributes", "group.category")) + + try: + fighter = Fighter(item) + except ValueError: + pyfalog.warning("Invalid fighter: {}", item) + return False + + if not fighter.fits(fit): + return False + + used = fit.getSlotsUsed(fighter.slot) + total = fit.getNumSlots(fighter.slot) + + if used >= total: + fighter.active = False + + fit.fighters.append(fighter) + self.new_index = fit.fighters.index(fighter) + + eos.db.commit() + + return True + + def Undo(self): + from .fitRemoveFighter import FitRemoveFighterCommand # Avoid circular import + cmd = FitRemoveFighterCommand(self.fitID, self.new_index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddImplant.py b/gui/fitCommands/calc/fitAddImplant.py new file mode 100644 index 000000000..4e3fd372c --- /dev/null +++ b/gui/fitCommands/calc/fitAddImplant.py @@ -0,0 +1,48 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.implant import Implant +pyfalog = Logger(__name__) + + +class FitAddImplantCommand(wx.Command): + """" + from sFit.addImplant + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.itemID = itemID + self.old_item = None + + def Do(self): + pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", self.fitID, self.itemID) + + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager="attributes") + + if next((x for x in fit.implants if x.itemID == self.itemID), None): + return False # already have item in list of implants + + try: + implant = Implant(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", self.itemID) + return False + + self.old_item = fit.implants.makeRoom(implant) + fit.implants.append(implant) + self.new_index = fit.implants.index(implant) + return True + + def Undo(self): + if self.old_item: + # If we had an item in the slot previously, add it back. + cmd = FitAddImplantCommand(self.fitID, self.old_item) + cmd.Do() + return True + + from .fitRemoveImplant import FitRemoveImplantCommand # Avoid circular import + cmd = FitRemoveImplantCommand(self.fitID, self.new_index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddModule.py b/gui/fitCommands/calc/fitAddModule.py new file mode 100644 index 000000000..cd96b6d4f --- /dev/null +++ b/gui/fitCommands/calc/fitAddModule.py @@ -0,0 +1,83 @@ +import wx +from eos.saveddata.module import Module, State +import eos.db +from logbook import Logger +from service.fit import Fit +pyfalog = Logger(__name__) + + +class FitAddModuleCommand(wx.Command): + """" + Fitting command that appends a module to a fit using the first available slot. In the case of a Subsystem, it checks + if there is already a subsystem with the same slot, and runs the replace command instead. + + from sFit.appendModule + """ + def __init__(self, fitID, itemID, mutaplasmidID=None, baseID=None): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.mutaplasmidID = mutaplasmidID + self.baseID = baseID + self.new_position = None + self.change = None + self.replace_cmd = None + + def Do(self): + sFit = Fit.getInstance() + fitID = self.fitID + itemID = self.itemID + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager=("attributes", "group.category")) + + bItem = eos.db.getItem(self.baseID) if self.baseID else None + mItem = next((x for x in bItem.mutaplasmids if x.ID == self.mutaplasmidID)) if self.mutaplasmidID else None + + try: + self.module = Module(item, bItem, mItem) + except ValueError: + pyfalog.warning("Invalid module: {}", item) + return False + + # If subsystem and we need to replace, run the replace command instead and bypass the rest of this command + if self.module.item.category.name == "Subsystem": + for mod in fit.modules: + if mod.getModifiedItemAttr("subSystemSlot") == self.module.getModifiedItemAttr("subSystemSlot"): + from .fitReplaceModule import FitReplaceModuleCommand + self.replace_cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, itemID) + return self.replace_cmd.Do() + + if self.module.fits(fit): + pyfalog.debug("Adding {} as module for fit {}", self.module, fit) + self.module.owner = fit + numSlots = len(fit.modules) + fit.modules.append(self.module) + if self.module.isValidState(State.ACTIVE): + self.module.state = State.ACTIVE + + # todo: fix these + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + # self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + sFit.checkStates(fit, self.module) + + # fit.fill() + eos.db.commit() + + self.change = numSlots != len(fit.modules) + self.new_position = self.module.modPosition + else: + return False + + return True + + def Undo(self): + # We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead + if self.replace_cmd: + return self.replace_cmd.Undo() + + from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import + if self.new_position: + cmd = FitRemoveModuleCommand(self.fitID, [self.new_position]) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddProjectedDrone.py b/gui/fitCommands/calc/fitAddProjectedDrone.py new file mode 100644 index 000000000..6c2ddf270 --- /dev/null +++ b/gui/fitCommands/calc/fitAddProjectedDrone.py @@ -0,0 +1,45 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.drone import Drone +pyfalog = Logger(__name__) + + +class FitAddProjectedDroneCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.index = None + + def Do(self): + pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID) + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID) + + drone = None + for d in fit.projectedDrones.find(item): + if d is None or d.amountActive == d.amount or d.amount >= 5: + drone = d + break + + if drone is None: + drone = Drone(item) + if not drone.item.isType("projected"): + return False + fit.projectedDrones.append(drone) + + self.index = fit.projectedDrones.index(drone) + drone.amount += 1 + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand # avoids circular import + cmd = FitRemoveProjectedDroneCommand(self.fitID, self.index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddProjectedEnv.py b/gui/fitCommands/calc/fitAddProjectedEnv.py new file mode 100644 index 000000000..dc1a0443a --- /dev/null +++ b/gui/fitCommands/calc/fitAddProjectedEnv.py @@ -0,0 +1,51 @@ +import wx +from eos.saveddata.module import Module, State +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitAddProjectedEnvCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.new_index = None + self.old_item = None + + def Do(self): + pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID) + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager=("attributes", "group.category")) + + try: + module = Module(item) + except ValueError: + return False + + # todo: thing to check for existing environmental effects + + module.state = State.ONLINE + if module.isExclusiveSystemEffect: + # if this is an exclusive system effect, we need to cache the old one. We make room for the new one here, which returns the old one + self.old_item = fit.projectedModules.makeRoom(module) + + fit.projectedModules.append(module) + eos.db.commit() + self.new_index = fit.projectedModules.index(module) + return True + + def Undo(self): + if self.old_item: + # If we had an item in the slot previously, add it back. + cmd = FitAddProjectedEnvCommand(self.fitID, self.old_item) + cmd.Do() + return True + from gui.fitCommands.calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand # avoids circular import + cmd = FitRemoveProjectedEnvCommand(self.fitID, self.itemID) + cmd.Do() + + return True diff --git a/gui/fitCommands/calc/fitAddProjectedFighter.py b/gui/fitCommands/calc/fitAddProjectedFighter.py new file mode 100644 index 000000000..17d50e14b --- /dev/null +++ b/gui/fitCommands/calc/fitAddProjectedFighter.py @@ -0,0 +1,40 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.fighter import Fighter +pyfalog = Logger(__name__) + + +class FitAddProjectedFighterCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.new_index = None + + def Do(self): + pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID) + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager=("attributes", "group.category")) + + try: + fighter = Fighter(item) + except ValueError: + return False + + fit.projectedFighters.append(fighter) + # sometimes fighters aren't added because they aren't valid projectionable ones. todo: move that logic into here + if fighter in fit.projectedFighters: + eos.db.commit() + self.new_index = fit.projectedFighters.index(fighter) + return True + return False + + def Undo(self): + from gui.fitCommands.calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand # avoids circular import + cmd = FitRemoveProjectedFighterCommand(self.fitID, self.new_index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddProjectedFit.py b/gui/fitCommands/calc/fitAddProjectedFit.py new file mode 100644 index 000000000..cd1e1a7f9 --- /dev/null +++ b/gui/fitCommands/calc/fitAddProjectedFit.py @@ -0,0 +1,39 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitAddProjectedFitCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, projectedFitID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.projectedFitID = projectedFitID + self.new_index = None + self.old_item = None + + def Do(self): + pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.projectedFitID) + fit = eos.db.getFit(self.fitID) + projectedFit = eos.db.getFit(self.projectedFitID) + + if projectedFit is None or projectedFit in fit.projectedFits: + return False + + fit.projectedFitDict[projectedFit.ID] = projectedFit + + # this bit is required -- see GH issue # 83 + eos.db.saveddata_session.flush() + eos.db.saveddata_session.refresh(projectedFit) + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand # avoids circular import + cmd = FitRemoveProjectedFitCommand(self.fitID, self.projectedFitID) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitAddProjectedModule.py b/gui/fitCommands/calc/fitAddProjectedModule.py new file mode 100644 index 000000000..6d2ad3f4f --- /dev/null +++ b/gui/fitCommands/calc/fitAddProjectedModule.py @@ -0,0 +1,43 @@ +import wx +import eos.db +from logbook import Logger +from eos.saveddata.module import Module, State +pyfalog = Logger(__name__) + + +class FitAddProjectedModuleCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.new_index = None + + def Do(self): + pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID) + fit = eos.db.getFit(self.fitID) + item = eos.db.getItem(self.itemID, eager=("attributes", "group.category")) + + try: + module = Module(item) + if not module.item.isType("projected"): + return False + except ValueError: + return False + + module.state = State.ACTIVE + if not module.canHaveState(module.state, fit): + module.state = State.OFFLINE + fit.projectedModules.append(module) + + eos.db.commit() + self.new_index = fit.projectedModules.index(module) + return True + + def Undo(self): + from gui.fitCommands.calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand # avoids circular import + cmd = FitRemoveProjectedModuleCommand(self.fitID, self.new_index) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitChangeCargoQty.py b/gui/fitCommands/calc/fitChangeCargoQty.py new file mode 100644 index 000000000..2ba30e085 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeCargoQty.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeCargoQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.position = position + self.amount = amount # add x amount. If this goes over amount, removes stack + self.old_amount = None + + def Do(self): + pyfalog.debug("Changing cargo ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount) + fit = eos.db.getFit(self.fitID) + cargo = fit.cargo[self.position] + self.old_amount = cargo.amount + cargo.amount = self.amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitChangeCargoQty(self.fitID, self.position, self.old_amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeDroneQty.py b/gui/fitCommands/calc/fitChangeDroneQty.py new file mode 100644 index 000000000..8f7e8e801 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeDroneQty.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeDroneQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.position = position + self.amount = amount # add x amount. If this goes over amount, removes stack + self.old_amount = None + + def Do(self): + pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount) + fit = eos.db.getFit(self.fitID) + drone = fit.drones[self.position] + self.old_amount = drone.amount + drone.amount = self.amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitChangeDroneQty(self.fitID, self.position, self.old_amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeDroneVariation.py b/gui/fitCommands/calc/fitChangeDroneVariation.py new file mode 100644 index 000000000..7f277895a --- /dev/null +++ b/gui/fitCommands/calc/fitChangeDroneVariation.py @@ -0,0 +1,39 @@ +import wx +from logbook import Logger + +import eos.db +pyfalog = Logger(__name__) + + +class FitChangeDroneVariationCommand(wx.Command): + """" + Fitting command that changes an existing drone into another variation. + """ + def __init__(self, fitID, position, itemID): + wx.Command.__init__(self, True, "Change Module") + + self.fitID = fitID + self.itemID = itemID + self.position = position + self.old_drone = None + + def Do(self): + return self.change_drone(self.fitID, self.position, self.itemID) + + def Undo(self): + self.change_drone(self.fitID, self.position, self.old_drone) + return True + + def change_drone(self, fitID, position, itemID): + fit = eos.db.getFit(self.fitID) + drone = fit.drones[self.position] + + if itemID == drone.itemID: + return False + + self.old_drone = drone.itemID + + drone.changeType(itemID) + eos.db.commit() + # todo: ensure that, whatever type we send in, is actually a variation of the original drone. If not, return False + return True diff --git a/gui/fitCommands/calc/fitChangeFighterQty.py b/gui/fitCommands/calc/fitChangeFighterQty.py new file mode 100644 index 000000000..4cef20034 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeFighterQty.py @@ -0,0 +1,30 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeFighterQty(wx.Command): + """" + from sFit.changeActiveFighters + """ + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.position = position + self.amount = amount # add x amount. If this goes over amount, removes stack + self.old_amount = None + + def Do(self): + pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount) + fit = eos.db.getFit(self.fitID) + fighter = fit.fighters[self.position] + self.old_amount = fighter.amountActive + fighter.amountActive = self.amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitChangeFighterQty(self.fitID, self.position, self.old_amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeImplantLocation.py b/gui/fitCommands/calc/fitChangeImplantLocation.py new file mode 100644 index 000000000..fc1dec890 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeImplantLocation.py @@ -0,0 +1,25 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeImplantLocation(wx.Command): + def __init__(self, fitID, source): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.source = source + self.old_source = None + + def Do(self): + pyfalog.debug("Toggling implant source for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + self.old_source = fit.implantSource + fit.implantSource = self.source + eos.db.commit() + return True + + + def Undo(self): + cmd = FitChangeImplantLocation(self.fitID, self.old_source) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeProjectedDroneQty.py b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py new file mode 100644 index 000000000..3ca926c17 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeProjectedDroneQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.position = position + self.amount = amount # add x amount. If this goes over amount, removes stack + self.old_amount = None + + def Do(self): + pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount) + fit = eos.db.getFit(self.fitID) + drone = fit.projectedDrones[self.position] + self.old_amount = drone.amount + drone.amount = self.amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.old_amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeProjectedFitQty.py b/gui/fitCommands/calc/fitChangeProjectedFitQty.py new file mode 100644 index 000000000..aa75e8491 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeProjectedFitQty.py @@ -0,0 +1,36 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitChangeProjectedFitQty(wx.Command): + """" + from sFit.changeAmount + """ + def __init__(self, fitID, pfitID, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.pfitID = pfitID + self.amount = amount + self.old_amount = None + + def Do(self): + pfit = eos.db.getFit(self.pfitID) + + if not pfit: # fit was deleted + return False + + amount = min(20, max(1, self.amount)) # 1 <= a <= 20 + + projectionInfo = pfit.getProjectionInfo(self.fitID) + if projectionInfo: + self.old_amount = projectionInfo.amount + projectionInfo.amount = amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.old_amount) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitChangeState.py b/gui/fitCommands/calc/fitChangeState.py new file mode 100644 index 000000000..0b901e640 --- /dev/null +++ b/gui/fitCommands/calc/fitChangeState.py @@ -0,0 +1,76 @@ +import wx +from logbook import Logger + +import gui.mainFrame +from eos.saveddata.module import Module +from service.fit import Fit +import eos.db + +pyfalog = Logger(__name__) + + +class FitChangeStatesCommand(wx.Command): + """ + Fitting command that trys to change the state of modules in [positions]. We use the base module to determine the + state that we will try to apply for all modules. + + + """ + def __init__(self, fitID, baseModPos, positions, click): + # todo: instead of modules, needs to be positions. Dead objects are a thing + wx.Command.__init__(self, True, "Module State Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.baseModPos = baseModPos + self.positions = positions + self.click = click + self.changed = None + self.old_states = {} + + def Do(self): + fit = eos.db.getFit(self.fitID) + sFit = Fit.getInstance() + baseMod = fit.modules[self.baseModPos] + + # make sure positions only include non-empty positions + self.positions = [x for x in self.positions if not fit.modules[x].isEmpty] + + for x in self.positions: + self.old_states[x] = fit.modules[x].state + + proposedState = Module.getProposedState(baseMod, self.click) + pyfalog.debug("Attempting to change modules to {}", proposedState) + + if proposedState != baseMod.state: + pyfalog.debug("Toggle {} state: {} for fit ID: {}", baseMod, proposedState, self.fitID) + + self.changed = True + baseMod.state = proposedState + for i in [x for x in self.positions if x != self.baseModPos]: # dont consider base module position + mod = fit.modules[i] + p = Module.getProposedState(mod, self.click, proposedState) + mod.state = p + if p != mod.state: + pyfalog.debug("Toggle {} state: {} for fit ID: {}", mod, p, self.fitID) + self.changed = True + + # if we haven't change the state (eg, overheat -> overheat), simply fail the command + if self.changed: + eos.db.commit() + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + # self.recalc(fit) + # # Then, check states of all modules and change where needed. This will recalc if needed + sFit.checkStates(fit, baseMod) + # self.checkStates(fit, base) + return True + return False + + def Undo(self): + # todo: some sanity checking to make sure that we are applying state back to the same modules? + fit = self.sFit.getFit(self.fitID) + for k, v in self.old_states.items(): + mod = fit.modules[k] + pyfalog.debug("Reverting {} to state {} for fit ID", mod, v, self.fitID) + mod.state = v + return True diff --git a/gui/fitCommands/calc/fitCloneModule.py b/gui/fitCommands/calc/fitCloneModule.py new file mode 100644 index 000000000..519b653ef --- /dev/null +++ b/gui/fitCommands/calc/fitCloneModule.py @@ -0,0 +1,44 @@ +import wx +import eos.db +from logbook import Logger +import copy +pyfalog = Logger(__name__) + + +class FitCloneModuleCommand(wx.Command): + """ + Clone a module from src to dst + This will overwrite dst! Checking for empty module must be + done at a higher level + + from sFit.cloneModule + """ + def __init__(self, fitID, src, dst): + wx.Command.__init__(self, True, "Module Clone") + self.fitID = fitID + self.src = src + self.dst = dst + + def Do(self): + fit = eos.db.getFit(self.fitID) + # Gather modules + srcMod = fit.modules[self.src] + dstMod = fit.modules[self.dst] # should be a placeholder module + + new = copy.deepcopy(srcMod) + new.owner = fit + if new.fits(fit): + pyfalog.debug("Cloning {} from source {} to destination {} for fit ID {}", srcMod, self.src, self.dst, self.fitID) + # insert copy if module meets hardpoint restrictions + fit.modules.remove(dstMod) + fit.modules.insert(self.dst, new) + + eos.db.commit() + return True + return False + + def Undo(self): + from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import + cmd = FitRemoveModuleCommand(self.fitID, [self.dst]) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py new file mode 100644 index 000000000..3a28c68ea --- /dev/null +++ b/gui/fitCommands/calc/fitImportMutatedModule.py @@ -0,0 +1,93 @@ +import wx +from eos.saveddata.module import Module, State +import eos.db +from eos.db.gamedata.queries import getDynamicItem +from logbook import Logger +from service.fit import Fit +pyfalog = Logger(__name__) + + +class FitImportMutatedCommand(wx.Command): + """" + Fitting command that takes info about mutated module, composes it and adds it to a fit + """ + def __init__(self, fitID, baseItem, mutaItem, attrMap): + wx.Command.__init__(self, True) + self.fitID = fitID + self.baseItem = baseItem + self.mutaItem = mutaItem + self.attrMap = attrMap + self.new_position = None + self.change = None + self.replace_cmd = None + + def Do(self): + sFit = Fit.getInstance() + fitID = self.fitID + fit = eos.db.getFit(fitID) + + if self.baseItem is None: + pyfalog.warning("Unable to build non-mutated module: no base item to build from") + return False + + try: + mutaTypeID = self.mutaItem.ID + except AttributeError: + mutaplasmid = None + else: + mutaplasmid = getDynamicItem(mutaTypeID) + # Try to build simple item even though no mutaplasmid found + if mutaplasmid is None: + try: + module = Module(self.baseItem) + except ValueError: + pyfalog.warning("Unable to build non-mutated module: {}", self.baseItem) + return False + # Build mutated module otherwise + else: + try: + module = Module(mutaplasmid.resultingItem, self.baseItem, mutaplasmid) + except ValueError: + pyfalog.warning("Unable to build mutated module: {} {}", self.baseItem, self.mutaItem) + return False + else: + for attrID, mutator in module.mutators.items(): + if attrID in self.attrMap: + mutator.value = self.attrMap[attrID] + + + # this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere? + if module.fits(fit): + pyfalog.debug("Adding {} as module for fit {}", module, fit) + module.owner = fit + numSlots = len(fit.modules) + fit.modules.append(module) + if module.isValidState(State.ACTIVE): + module.state = State.ACTIVE + + # todo: fix these + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + # self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + sFit.checkStates(fit, module) + + # fit.fill() + eos.db.commit() + + self.change = numSlots != len(fit.modules) + self.new_position = module.modPosition + else: + return False + + return True + + def Undo(self): + # We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead + if self.replace_cmd: + return self.replace_cmd.Undo() + + from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import + if self.new_position is not None: + cmd = FitRemoveModuleCommand(self.fitID, [self.new_position]) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveBooster.py b/gui/fitCommands/calc/fitRemoveBooster.py new file mode 100644 index 000000000..14733c93b --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveBooster.py @@ -0,0 +1,30 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveBoosterCommand(wx.Command): + """" + from sFit.removeBooster + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Implant remove") + self.fitID = fitID + self.position = position + self.old = None + + def Do(self): + pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", self.position, self.fitID) + + fit = eos.db.getFit(self.fitID) + booster = fit.boosters[self.position] + self.old = booster.itemID + fit.boosters.remove(booster) + return True + + def Undo(self): + from .fitAddBooster import FitAddBoosterCommand # Avoid circular import + cmd = FitAddBoosterCommand(self.fitID, self.old) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveCargo.py b/gui/fitCommands/calc/fitRemoveCargo.py new file mode 100644 index 000000000..779c7c757 --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveCargo.py @@ -0,0 +1,47 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveCargoCommand(wx.Command): + """" + Fitting command that sets the amount for an item within the cargo. + + from sFit.removeCargo + """ + def __init__(self, fitID, itemID, amount=1, stack=False): + wx.Command.__init__(self, True, "Cargo remove") + self.fitID = fitID + self.itemID = itemID + self.stack = stack # remove entire stack + self.amount = amount # remove x amount. If this goes over amount, removes stack + self.old_amount = None + + def Do(self): + pyfalog.debug("Removing cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID) + fit = eos.db.getFit(self.fitID) + cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None) + + if cargo is None: + return False + + self.old_amount = cargo.amount + + if self.amount >= cargo.amount: + self.stack = True # set to full stack, this allows easier logic in the Undo function + + if self.stack or self.amount >= cargo.amount: + fit.cargo.remove(cargo) + eos.db.commit() + return True + + cargo.amount -= self.amount + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddCargo import FitAddCargoCommand # Avoid circular import + cmd = FitAddCargoCommand(self.fitID, self.itemID, self.old_amount, True) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveCommand.py b/gui/fitCommands/calc/fitRemoveCommand.py new file mode 100644 index 000000000..29e681b60 --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveCommand.py @@ -0,0 +1,37 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveCommandCommand(wx.Command): # well that's an unfortunate name + """" + from sFit.removeCommand + """ + def __init__(self, fitID, commandFitID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.commandFitID = commandFitID + + def Do(self): + pyfalog.debug("Removing command projection from fit ({0}) for: {1}", self.fitID, self.commandFitID) + fit = eos.db.getFit(self.fitID) + command = eos.db.getFit(self.commandFitID) + if not command: + return False + del fit.commandFitDict[command.ID] + + eos.db.commit() + return True + + def Undo(self): + command = eos.db.getFit(self.commandFitID) + + if not command: + # can't find the command fit, it must have been deleted. Just skip this undo + return True + + from .fitAddCommand import FitAddCommandCommand + cmd = FitAddCommandCommand(self.fitID, self.commandFitID) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveDrone.py b/gui/fitCommands/calc/fitRemoveDrone.py new file mode 100644 index 000000000..4e1fb0a6a --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveDrone.py @@ -0,0 +1,43 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveDroneCommand(wx.Command): + """" + from sFit.addDrone + """ + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Drone add") + self.fitID = fitID + self.position = position + self.amount = amount # add x amount. If this goes over amount, removes stack + self.removed_item = None + + def Do(self): + pyfalog.debug("Removing {0} drones for fit ID: {1}", self.amount, self.fitID) + fit = eos.db.getFit(self.fitID) + d = fit.drones[self.position] + d.amount -= self.amount + if d.amountActive > 0: + d.amountActive -= self.amount + + if d.amount == 0: + self.removed_item = d.itemID + del fit.drones[self.position] + + eos.db.commit() + return True + + def Undo(self): + if self.removed_item: + from .fitAddDrone import FitAddDroneCommand # Avoid circular import + cmd = FitAddDroneCommand(self.fitID, self.removed_item, self.amount) + return cmd.Do() + else: + fit = eos.db.getFit(self.fitID) + d = fit.drones[self.position] + d.amount += self.amount + eos.db.commit() + return True diff --git a/gui/fitCommands/calc/fitRemoveFighter.py b/gui/fitCommands/calc/fitRemoveFighter.py new file mode 100644 index 000000000..da186bd48 --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveFighter.py @@ -0,0 +1,34 @@ +import wx +from logbook import Logger + +import eos.db + +pyfalog = Logger(__name__) + + +class FitRemoveFighterCommand(wx.Command): + """" + Fitting command that removes a module at a specified positions + + from sFit.removeFighter + """ + def __init__(self, fitID: int, position: int): + wx.Command.__init__(self, True) + self.fitID = fitID + self.position = position + self.change = None + self.removed_item = None + + def Do(self): + fitID = self.fitID + fit = eos.db.getFit(fitID) + f = fit.fighters[self.position] + fit.fighters.remove(f) + self.removed_item = f.itemID + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddFighter import FitAddFighterCommand # avoids circular import + cmd = FitAddFighterCommand(self.fitID, self.removed_item) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitRemoveImplant.py b/gui/fitCommands/calc/fitRemoveImplant.py new file mode 100644 index 000000000..d0f8101cd --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveImplant.py @@ -0,0 +1,32 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveImplantCommand(wx.Command): + """" + Fitting command that sets the amount for an item within the cargo. + + from sFit.removeImplant + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Implant remove") + self.fitID = fitID + self.position = position + self.old_implant = None + + def Do(self): + pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", self.position, self.fitID) + + fit = eos.db.getFit(self.fitID) + implant = fit.implants[self.position] + self.old_implant = implant.itemID + fit.implants.remove(implant) + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddImplant import FitAddImplantCommand # Avoid circular import + cmd = FitAddImplantCommand(self.fitID, self.old_implant) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveModule.py b/gui/fitCommands/calc/fitRemoveModule.py new file mode 100644 index 000000000..5134f2f1b --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveModule.py @@ -0,0 +1,59 @@ +import wx + +from gui.fitCommands.helpers import ModuleInfoCache +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveModuleCommand(wx.Command): + """" + Fitting command that removes a module at a specified positions + + from sFit.removeModule + """ + def __init__(self, fitID: int, positions: list = None): + wx.Command.__init__(self, True) + self.fitID = fitID + self.positions = positions + self.modCache = [] + self.change = None + + def Do(self): + fitID = self.fitID + fit = eos.db.getFit(fitID) + + pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", self.positions, fitID) + + for x in self.positions: + mod = fit.modules[x] + if not mod.isEmpty: + pyfalog.debug(" -- Removing {}", mod) + self.modCache.append(ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID)) + fit.modules.toDummy(x) + + # if no modules have changes, skip command + if not len(self.modCache) > 0: + return False + + numSlots = len(fit.modules) + # todo: determine if we need to do this still + # self.recalc(fit) + # self.checkStates(fit, None) + # fit.fill() + eos.db.commit() + self.slotsChanged = numSlots != len(fit.modules) + return True + + def Undo(self): + pyfalog.debug("Reapplying {} removed module(s) for {}", len(self.modCache), self.fitID) + + from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand # avoids circular import + for mod in self.modCache: + pyfalog.debug(" -- {}", mod) + # todo, send the state and charge? + cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, mod.itemID) + cmd.Do() + cmd.module.state = mod.state + cmd.module.charge = mod.charge + return True diff --git a/gui/fitCommands/calc/fitRemoveProjectedDrone.py b/gui/fitCommands/calc/fitRemoveProjectedDrone.py new file mode 100644 index 000000000..9d70ade2e --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveProjectedDrone.py @@ -0,0 +1,42 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +# this has the same exact definition that regular projected modules, besides the undo +class FitRemoveProjectedDroneCommand(wx.Command): + """" + from sFit.project + """ + + def __init__(self, fitID, position, stack=False): + wx.Command.__init__(self, True) + self.fitID = fitID + self.position = position + self.removed_item = None + self.stack = stack + + def Do(self): + pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position) + fit = eos.db.getFit(self.fitID) + + drone = fit.projectedDrones[self.position] + if self.stack: + fit.projectedDrones.remove(drone) + else: + if drone.amount > 1: + drone.amount -= 1 + else: + fit.projectedDrones.remove(drone) + + self.drone_item = drone.itemID + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddProjectedDrone import FitAddProjectedDroneCommand + cmd = FitAddProjectedDroneCommand(self.fitID, self.drone_item) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveProjectedEnv.py b/gui/fitCommands/calc/fitRemoveProjectedEnv.py new file mode 100644 index 000000000..61b6ee939 --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveProjectedEnv.py @@ -0,0 +1,35 @@ +import wx +import eos.db +from logbook import Logger +from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand +pyfalog = Logger(__name__) + + +# this has the same exact definition that regular rpojected modules, besides the undo +class FitRemoveProjectedEnvCommand(FitRemoveProjectedModuleCommand): + """" + from sFit.project + """ + + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.itemID = itemID + self.removed_item = None + + def Do(self): + pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.itemID) + fit = eos.db.getFit(self.fitID) + + item = next((x for x in fit.projectedModules if x.itemID == self.itemID), None) + self.removed_item = item.itemID + fit.projectedModules.remove(item) + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddProjectedEnv import FitAddProjectedEnvCommand + cmd = FitAddProjectedEnvCommand(self.fitID, self.removed_item) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveProjectedFighter.py b/gui/fitCommands/calc/fitRemoveProjectedFighter.py new file mode 100644 index 000000000..d3550b43e --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveProjectedFighter.py @@ -0,0 +1,34 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +# this has the same exact definition that regular rpojected modules, besides the undo +class FitRemoveProjectedFighterCommand(wx.Command): + """" + from sFit.project + """ + + def __init__(self, fitID, position): + wx.Command.__init__(self, True) + self.fitID = fitID + self.position = position + self.removed_item = None + + def Do(self): + pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position) + fit = eos.db.getFit(self.fitID) + + fighter = fit.projectedFighters[self.position] + fit.projectedFighters.remove(fighter) + self.removed_item = fighter.itemID + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddProjectedFighter import FitAddProjectedFighterCommand + cmd = FitAddProjectedFighterCommand(self.fitID, self.removed_item) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveProjectedFit.py b/gui/fitCommands/calc/fitRemoveProjectedFit.py new file mode 100644 index 000000000..66d62ab10 --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveProjectedFit.py @@ -0,0 +1,37 @@ +import wx +import eos.db +from logbook import Logger +from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand +pyfalog = Logger(__name__) + + +# this has the same exact definition that regular rpojected modules, besides the undo +class FitRemoveProjectedFitCommand(FitRemoveProjectedModuleCommand): + """" + from sFit.project + """ + + def __init__(self, fitID, projectedFitID): + wx.Command.__init__(self, True) + self.fitID = fitID + self.projectedFitID = projectedFitID + + def Do(self): + pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.projectedFitID) + fit = eos.db.getFit(self.fitID) + projectedFit = eos.db.getFit(self.projectedFitID) + + if projectedFit is None: + return False + + del fit.projectedFitDict[projectedFit.ID] + + eos.db.commit() + return True + + def Undo(self): + # todo: figure out if I need to return false here if the fit doesn't return true (means it was deleted) + from gui.fitCommands.calc.fitAddProjectedFit import FitAddProjectedFitCommand + cmd = FitAddProjectedFitCommand(self.fitID, self.projectedFitID) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRemoveProjectedModule.py b/gui/fitCommands/calc/fitRemoveProjectedModule.py new file mode 100644 index 000000000..46f8dadba --- /dev/null +++ b/gui/fitCommands/calc/fitRemoveProjectedModule.py @@ -0,0 +1,30 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitRemoveProjectedModuleCommand(wx.Command): + """" + from sFit.project + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True) + self.fitID = fitID + self.position = position + self.removed_item = None + + def Do(self): + pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position) + fit = eos.db.getFit(self.fitID) + self.removed_item = fit.projectedModules[self.position].itemID + del fit.projectedModules[self.position] + + eos.db.commit() + return True + + def Undo(self): + from gui.fitCommands.calc.fitAddProjectedModule import FitAddProjectedModuleCommand + cmd = FitAddProjectedModuleCommand(self.fitID, self.removed_item) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitRename.py b/gui/fitCommands/calc/fitRename.py new file mode 100644 index 000000000..38a72c519 --- /dev/null +++ b/gui/fitCommands/calc/fitRename.py @@ -0,0 +1,26 @@ +import wx +from logbook import Logger + +import eos.db + +pyfalog = Logger(__name__) + + +class FitRenameCommand(wx.Command): + def __init__(self, fitID, newName): + wx.Command.__init__(self, True, "FitRename") + self.fitID = fitID + self.newName = newName + self.oldName = None + + def Do(self): + pyfalog.debug("Renaming fit ({0}) to: {1}", self.fitID, self.newName) + fit = eos.db.getFit(self.fitID) + self.oldName = fit.name + fit.name = self.newName + eos.db.commit() + return True + + def Undo(self): + cmd = FitRenameCommand(self.fitID, self.oldName) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitReplaceModule.py b/gui/fitCommands/calc/fitReplaceModule.py new file mode 100644 index 000000000..286bd9451 --- /dev/null +++ b/gui/fitCommands/calc/fitReplaceModule.py @@ -0,0 +1,90 @@ +import wx +from logbook import Logger + +import eos.db +from eos.saveddata.module import Module, State +from gui.fitCommands.helpers import ModuleInfoCache + +pyfalog = Logger(__name__) + + +class FitReplaceModuleCommand(wx.Command): + """" + Fitting command that changes an existing module into another. + + from sFit.changeModule + """ + def __init__(self, fitID, position, itemID): + wx.Command.__init__(self, True, "Change Module") + self.fitID = fitID + self.itemID = itemID + self.position = position + self.module = None # the module version of itemID + self.old_module = None + + def Do(self): + fit = eos.db.getFit(self.fitID) + + mod = fit.modules[self.position] + if not mod.isEmpty: + self.old_module = ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, + mod.mutaplasmidID) + + return self.change_module(self.fitID, self.position, self.itemID) + + def Undo(self): + if self.old_module is None: + fit = eos.db.getFit(self.fitID) + fit.modules.toDummy(self.position) + return True + self.change_module(self.fitID, self.position, self.old_module.itemID) + self.module.state = self.old_module.state + self.module.charge = self.old_module.charge + return True + + def change_module(self, fitID, position, itemID): + fit = eos.db.getFit(fitID) + + # We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot. + # todo: evaluate if this is still a thing + # actually, this seems like it should be handled higher up... + # + # if self.isAmmo(itemID): + # module = fit.modules[self.position] + # if not module.isEmpty: + # self.setAmmo(fitID, itemID, [module]) + # return True + + pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", self.position, fitID) + + item = eos.db.getItem(itemID, eager=("attributes", "group.category")) + mod = fit.modules[self.position] + + try: + self.module = Module(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", itemID) + return False + + if self.module.slot != mod.slot: + return False + + # Dummy it out in case the next bit fails + fit.modules.toDummy(self.position) + + if self.module.fits(fit): + self.module.owner = fit + fit.modules.toModule(self.position, self.module) + if self.module.isValidState(State.ACTIVE): + self.module.state = State.ACTIVE + + if self.old_module and self.old_module.charge and self.module.isValidCharge(self.old_module.charge): + self.module.charge = self.old_module.charge + + # Then, check states of all modules and change where needed. This will recalc if needed + # self.checkStates(fit, m) + + # fit.fill() + eos.db.commit() + return True + return False diff --git a/gui/fitCommands/calc/fitSetCharge.py b/gui/fitCommands/calc/fitSetCharge.py new file mode 100644 index 000000000..91a4abe8d --- /dev/null +++ b/gui/fitCommands/calc/fitSetCharge.py @@ -0,0 +1,48 @@ +import wx +from logbook import Logger + +import eos.db +import gui.mainFrame +from service.fit import Fit + +pyfalog = Logger(__name__) + + +class FitSetChargeCommand(wx.Command): + def __init__(self, fitID, positions, chargeID=None, projected=False): + # todo: determine if this command really should be used with a group of modules, or a simple per module basis + wx.Command.__init__(self, True, "Module Charge Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.chargeID = chargeID + self.positions = positions + self.projected = projected + self.cache = None + + def Do(self): + return self.__setAmmo(self.positions, self.chargeID) + + def Undo(self): + for position, chargeID in self.cache.items(): + self.__setAmmo([position], chargeID) + return True + + def __setAmmo(self, positions, chargeID): + fit = eos.db.getFit(self.fitID) + source = fit.modules if not self.projected else fit.projectedModules + self.cache = {source[i].modPosition: source[i].chargeID for i in positions} + ammo = eos.db.getItem(chargeID) if chargeID else None + + if ammo is not None and not ammo.isCharge: + return False + result = False + + for pos in positions: + mod = source[pos] + if not mod.isEmpty and mod.isValidCharge(ammo): + pyfalog.debug("Set ammo {} for {} on fit {}", ammo, mod, self.fitID) + result = True + mod.charge = ammo + eos.db.commit() + return result diff --git a/gui/fitCommands/calc/fitSetMode.py b/gui/fitCommands/calc/fitSetMode.py new file mode 100644 index 000000000..750be78dd --- /dev/null +++ b/gui/fitCommands/calc/fitSetMode.py @@ -0,0 +1,28 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitSetModeCommand(wx.Command): + """" + from sFit.setMode + """ + def __init__(self, fitID, mode): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.mode = mode + self.old_mode = None + + def Do(self): + pyfalog.debug("Set mode for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + self.old_mode = fit.mode + fit.mode = self.mode + eos.db.commit() + return True + + def Undo(self): + cmd = FitSetModeCommand(self.fitID, self.old_mode) + cmd.Do() + return True diff --git a/gui/fitCommands/calc/fitSwapModule.py b/gui/fitCommands/calc/fitSwapModule.py new file mode 100644 index 000000000..38ed8d4eb --- /dev/null +++ b/gui/fitCommands/calc/fitSwapModule.py @@ -0,0 +1,38 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitSwapModuleCommand(wx.Command): + """" + from sFit.swapModules + """ + def __init__(self, fitID, src, dst): + wx.Command.__init__(self, True, "Module Swap") + self.fitID = fitID + self.src = src + self.dst = dst + + def Do(self): + self.__swap(self.fitID, self.src, self.dst) + return True + + def Undo(self): + self.__swap(self.fitID, self.dst, self.src) + return True + + def __swap(self, fitID, src, dst): + pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID) + fit = eos.db.getFit(fitID) + # Gather modules + srcMod = fit.modules[src] + dstMod = fit.modules[dst] + + # To swap, we simply remove mod and insert at destination. + fit.modules.remove(srcMod) + fit.modules.insert(dst, srcMod) + fit.modules.remove(dstMod) + fit.modules.insert(src, dstMod) + + eos.db.commit() diff --git a/gui/fitCommands/calc/fitToggleBooster.py b/gui/fitCommands/calc/fitToggleBooster.py new file mode 100644 index 000000000..53b56487b --- /dev/null +++ b/gui/fitCommands/calc/fitToggleBooster.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitToggleBoosterCommand(wx.Command): + """" + from sFit.toggleBooster + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.position = position + + def Do(self): + pyfalog.debug("Toggling booster for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + booster = fit.boosters[self.position] + booster.active = not booster.active + + eos.db.commit() + return True + + def Undo(self): + cmd = FitToggleBoosterCommand(self.fitID, self.position) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitToggleCommand.py b/gui/fitCommands/calc/fitToggleCommand.py new file mode 100644 index 000000000..c7d5a43ad --- /dev/null +++ b/gui/fitCommands/calc/fitToggleCommand.py @@ -0,0 +1,36 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitToggleCommandCommand(wx.Command): + """" + from sFit.toggleCommandFit + """ + def __init__(self, fitID, commandFitId): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.commandFitID = commandFitId + + def Do(self): + pyfalog.debug("Toggle command fit ({0}) for: {1}", self.commandFitID, self.fitID) + commandFit = eos.db.getFit(self.commandFitID) + + if not commandFit: + pyfalog.debug(" -- Command fit not found, deleted?") + return False + + commandInfo = commandFit.getCommandInfo(self.fitID) + + if not commandInfo: + pyfalog.debug(" -- Command fit info not found, deleted?") + return False + + commandInfo.active = not commandInfo.active + eos.db.commit() + return True + + def Undo(self): + cmd = FitToggleCommandCommand(self.fitID, self.commandFitID) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitToggleDrone.py b/gui/fitCommands/calc/fitToggleDrone.py new file mode 100644 index 000000000..b281be700 --- /dev/null +++ b/gui/fitCommands/calc/fitToggleDrone.py @@ -0,0 +1,30 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitToggleDroneCommand(wx.Command): + """" + from sFit.toggleDrone + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.position = position + + def Do(self): + pyfalog.debug("Toggling drones for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + d = fit.drones[self.position] + if d.amount == d.amountActive: + d.amountActive = 0 + else: + d.amountActive = d.amount + + eos.db.commit() + return True + + def Undo(self): + cmd = FitToggleDroneCommand(self.fitID, self.position) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitToggleFighter.py b/gui/fitCommands/calc/fitToggleFighter.py new file mode 100644 index 000000000..589b4bdd2 --- /dev/null +++ b/gui/fitCommands/calc/fitToggleFighter.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitToggleFighterCommand(wx.Command): + """" + from sFit.toggleFighter + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.position = position + + def Do(self): + pyfalog.debug("Toggling fighters for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + f = fit.fighters[self.position] + f.active = not f.active + + eos.db.commit() + return True + + def Undo(self): + cmd = FitToggleFighterCommand(self.fitID, self.position) + return cmd.Do() diff --git a/gui/fitCommands/calc/fitToggleImplant.py b/gui/fitCommands/calc/fitToggleImplant.py new file mode 100644 index 000000000..c004c297d --- /dev/null +++ b/gui/fitCommands/calc/fitToggleImplant.py @@ -0,0 +1,27 @@ +import wx +import eos.db +from logbook import Logger +pyfalog = Logger(__name__) + + +class FitToggleImplantCommand(wx.Command): + """" + from sFit.toggleImplant + """ + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Cargo add") + self.fitID = fitID + self.position = position + + def Do(self): + pyfalog.debug("Toggling implant for fit ID: {0}", self.fitID) + fit = eos.db.getFit(self.fitID) + implant = fit.implants[self.position] + implant.active = not implant.active + + eos.db.commit() + return True + + def Undo(self): + cmd = FitToggleImplantCommand(self.fitID, self.position) + return cmd.Do() diff --git a/gui/fitCommands/guiAddBooster.py b/gui/fitCommands/guiAddBooster.py new file mode 100644 index 000000000..fc732d857 --- /dev/null +++ b/gui/fitCommands/guiAddBooster.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddBooster import FitAddBoosterCommand + + +class GuiAddBoosterCommand(wx.Command): + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Booster Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + + def Do(self): + if self.internal_history.Submit(FitAddBoosterCommand(self.fitID, self.itemID)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddCargo.py b/gui/fitCommands/guiAddCargo.py new file mode 100644 index 000000000..4ff0f3447 --- /dev/null +++ b/gui/fitCommands/guiAddCargo.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddCargo import FitAddCargoCommand + + +class GuiAddCargoCommand(wx.Command): + def __init__(self, fitID, itemID, amount=1, replace=False): + wx.Command.__init__(self, True, "Cargo Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + self.amount = amount + self.replace = replace + + def Do(self): + if self.internal_history.Submit(FitAddCargoCommand(self.fitID, self.itemID, self.amount, self.replace)): + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddCharge.py b/gui/fitCommands/guiAddCharge.py new file mode 100644 index 000000000..ae2af59aa --- /dev/null +++ b/gui/fitCommands/guiAddCharge.py @@ -0,0 +1,32 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitSetCharge import FitSetChargeCommand + + +class GuiModuleAddChargeCommand(wx.Command): + def __init__(self, fitID, itemID, modules): + wx.Command.__init__(self, True, "Module Charge Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + self.positions = [mod.modPosition for mod in modules] + self.projected = modules[0].isProjected + + def Do(self): + if self.internal_history.Submit(FitSetChargeCommand(self.fitID, self.positions, self.itemID, self.projected)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddCommand.py b/gui/fitCommands/guiAddCommand.py new file mode 100644 index 000000000..06cfcbe98 --- /dev/null +++ b/gui/fitCommands/guiAddCommand.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddCommand import FitAddCommandCommand + + +class GuiAddCommandCommand(wx.Command): + def __init__(self, fitID, commandFitID): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.commandFitID = commandFitID + + def Do(self): + if self.internal_history.Submit(FitAddCommandCommand(self.fitID, self.commandFitID)): + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + self.sFit.recalc(self.fitID) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddDrone.py b/gui/fitCommands/guiAddDrone.py new file mode 100644 index 000000000..6549d1e21 --- /dev/null +++ b/gui/fitCommands/guiAddDrone.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddDrone import FitAddDroneCommand + + +class GuiAddDroneCommand(wx.Command): + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Cargo Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + + def Do(self): + cmd = FitAddDroneCommand(self.fitID, self.itemID) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddFighter.py b/gui/fitCommands/guiAddFighter.py new file mode 100644 index 000000000..01024e706 --- /dev/null +++ b/gui/fitCommands/guiAddFighter.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddFighter import FitAddFighterCommand + + +class GuiAddFighterCommand(wx.Command): + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Cargo Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + + def Do(self): + if self.internal_history.Submit(FitAddFighterCommand(self.fitID, self.itemID)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddImplant.py b/gui/fitCommands/guiAddImplant.py new file mode 100644 index 000000000..79b75f014 --- /dev/null +++ b/gui/fitCommands/guiAddImplant.py @@ -0,0 +1,32 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from eos.saveddata.fit import ImplantLocation +from .calc.fitAddImplant import FitAddImplantCommand +from .calc.fitChangeImplantLocation import FitChangeImplantLocation + + +class GuiAddImplantCommand(wx.Command): + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Implant Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + + def Do(self): + if self.internal_history.Submit(FitAddImplantCommand(self.fitID, self.itemID)) and self.internal_history.Submit(FitChangeImplantLocation(self.fitID, ImplantLocation.FIT)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiAddModule.py b/gui/fitCommands/guiAddModule.py new file mode 100644 index 000000000..f826566f4 --- /dev/null +++ b/gui/fitCommands/guiAddModule.py @@ -0,0 +1,68 @@ +import wx +import eos.db +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddModule import FitAddModuleCommand +from .calc.fitReplaceModule import FitReplaceModuleCommand +from .calc.fitSetCharge import FitSetChargeCommand +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiModuleAddCommand(wx.Command): + def __init__(self, fitID, itemID, position=None): + """ + Handles adding an item, usually a module, to the Fitting Window. + + :param fitID: The fit ID that we are modifying + :param itemID: The item that is to be added to the Fitting View. If this turns out to be a charge, we attempt to + set the charge on the underlying module (requires position) + :param position: Optional. The position in fit.modules that we are attempting to set the item to + """ + wx.Command.__init__(self, True, "Module Add: {}".format(itemID)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.itemID = itemID + self.internal_history = wx.CommandProcessor() + self.position = position + self.old_mod = None + + def Do(self): + pyfalog.debug("{} Do()".format(self)) + success = False + item = eos.db.getItem(self.itemID) + if item.isCharge and self.position is not None: + pyfalog.debug("Trying to add a charge") + success = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [self.position], self.itemID)) + if not success: + pyfalog.debug(" Failed") + return False # if it's a charge item and this failed, nothing more we can try. + # if we have a position set, try to apply the module to that position + elif self.position is not None: + pyfalog.debug("Trying to add a module to a specific position") + success = self.internal_history.Submit(FitReplaceModuleCommand(self.fitID, self.position, self.itemID)) + if not success: + pyfalog.debug(" Failed") + # something went wrong with trying to fit the module into specific location, attempt to append it + self.position = None + + # if we're not trying to set module to a position, simply append + if self.position is None: + pyfalog.debug("Trying to append a module") + success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID)) + + if success: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=self.itemID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=self.itemID)) + return True diff --git a/gui/fitCommands/guiAddProjected.py b/gui/fitCommands/guiAddProjected.py new file mode 100644 index 000000000..b2114e111 --- /dev/null +++ b/gui/fitCommands/guiAddProjected.py @@ -0,0 +1,56 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from eos.saveddata.module import Module +from .calc.fitAddProjectedModule import FitAddProjectedModuleCommand +from .calc.fitAddProjectedEnv import FitAddProjectedEnvCommand +from .calc.fitAddProjectedFit import FitAddProjectedFitCommand +from .calc.fitAddProjectedFighter import FitAddProjectedFighterCommand +from .calc.fitAddProjectedDrone import FitAddProjectedDroneCommand +from logbook import Logger +import eos.db +pyfalog = Logger(__name__) + + +class GuiAddProjectedCommand(wx.Command): + def __init__(self, fitID, id, type='item'): + wx.Command.__init__(self, True, "Projected Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.id = id + self.type = type + + def Do(self): + result = False + # since we can project various types, we need to switch of the fit command. We can't do this switch easily in + # the fit command since each type might have a different kind of undo, easier to split it out + if self.type == 'item': + item = eos.db.getItem(self.id, eager=("attributes", "group.category")) + + if item.category.name == "Drone": + result = self.internal_history.Submit(FitAddProjectedDroneCommand(self.fitID, self.id)) + elif item.category.name == "Fighter": + result = self.internal_history.Submit(FitAddProjectedFighterCommand(self.fitID, self.id)) + elif item.group.name in Module.SYSTEM_GROUPS: + result = self.internal_history.Submit(FitAddProjectedEnvCommand(self.fitID, self.id)) + else: + result = self.internal_history.Submit(FitAddProjectedModuleCommand(self.fitID, self.id)) + elif self.type == 'fit': + result = self.internal_history.Submit(FitAddProjectedFitCommand(self.fitID, self.id)) + + if result: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiCargoToModule.py b/gui/fitCommands/guiCargoToModule.py new file mode 100644 index 000000000..a2583e47c --- /dev/null +++ b/gui/fitCommands/guiCargoToModule.py @@ -0,0 +1,77 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from gui.fitCommands.calc.fitSetCharge import FitSetChargeCommand +from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand +from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand +from .calc.fitAddCargo import FitAddCargoCommand +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiCargoToModuleCommand(wx.Command): + """ + Moves cargo to fitting window. Can either do a copy, move, or swap with current module + If we try to copy/move into a spot with a non-empty module, we swap instead. + To avoid redundancy in converting Cargo item, this function does the + sanity checks as opposed to the GUI View. This is different than how the + normal .swapModules() does things, which is mostly a blind swap. + """ + + def __init__(self, fitID, moduleIdx, cargoIdx, copy=False): + wx.Command.__init__(self, True, "Module State Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.moduleIdx = moduleIdx + self.cargoIdx = cargoIdx + self.copy = copy + self.internal_history = wx.CommandProcessor() + + def Do(self): + sFit = Fit.getInstance() + fit = sFit.getFit(self.fitID) + module = fit.modules[self.moduleIdx] + cargo = fit.cargo[self.cargoIdx] + result = False + + # We're trying to move a charge from cargo to a slot. Use SetCharge command (don't respect move vs copy) + if sFit.isAmmo(cargo.item.ID): + result = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [module.modPosition], cargo.item.ID)) + else: + + pyfalog.debug("Moving cargo item to module for fit ID: {0}", self.fitID) + + self.addCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID) + + result = self.internal_history.Submit(self.addCmd) + + if not result: + # creating module failed for whatever reason + return False + + if self.addCmd.old_module is not None: + # we're swapping with an existing module, so remove cargo and add module + self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID) + result = self.internal_history.Submit(self.removeCmd) + + self.addCargoCmd = FitAddCargoCommand(self.fitID, self.addCmd.old_module.itemID) + result = self.internal_history.Submit(self.addCargoCmd) + elif not self.copy: + # move, not copying, so remove cargo + self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID) + result = self.internal_history.Submit(self.removeCmd) + + if result: + sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return result + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeCargoQty.py b/gui/fitCommands/guiChangeCargoQty.py new file mode 100644 index 000000000..80a906fc3 --- /dev/null +++ b/gui/fitCommands/guiChangeCargoQty.py @@ -0,0 +1,33 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeCargoQty import FitChangeCargoQty +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiChangeCargoQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.position = position + self.amount = amount + self.internal_history = wx.CommandProcessor() + + def Do(self): + cmd = FitChangeCargoQty(self.fitID, self.position, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeDroneQty.py b/gui/fitCommands/guiChangeDroneQty.py new file mode 100644 index 000000000..5aa2ea12e --- /dev/null +++ b/gui/fitCommands/guiChangeDroneQty.py @@ -0,0 +1,33 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeDroneQty import FitChangeDroneQty +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiChangeDroneQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.position = position + self.amount = amount + self.internal_history = wx.CommandProcessor() + + def Do(self): + cmd = FitChangeDroneQty(self.fitID, self.position, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeFighterQty.py b/gui/fitCommands/guiChangeFighterQty.py new file mode 100644 index 000000000..280bd1a16 --- /dev/null +++ b/gui/fitCommands/guiChangeFighterQty.py @@ -0,0 +1,34 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeFighterQty import FitChangeFighterQty +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiChangeFighterQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.position = position + self.amount = amount + self.internal_history = wx.CommandProcessor() + + def Do(self): + cmd = FitChangeFighterQty(self.fitID, self.position, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeImplantLocation.py b/gui/fitCommands/guiChangeImplantLocation.py new file mode 100644 index 000000000..5d6c57e9d --- /dev/null +++ b/gui/fitCommands/guiChangeImplantLocation.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeImplantLocation import FitChangeImplantLocation + + +class GuiChangeImplantLocation(wx.Command): + def __init__(self, fitID, source): + wx.Command.__init__(self, True, "Implant Source Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.source = source + + def Do(self): + if self.internal_history.Submit(FitChangeImplantLocation(self.fitID, self.source)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeProjectedDroneQty.py b/gui/fitCommands/guiChangeProjectedDroneQty.py new file mode 100644 index 000000000..f7ceaa657 --- /dev/null +++ b/gui/fitCommands/guiChangeProjectedDroneQty.py @@ -0,0 +1,34 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeProjectedDroneQty import FitChangeProjectedDroneQty +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiChangeProjectedDroneQty(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.position = position + self.amount = amount + self.internal_history = wx.CommandProcessor() + + def Do(self): + cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiChangeProjectedFitQty.py b/gui/fitCommands/guiChangeProjectedFitQty.py new file mode 100644 index 000000000..7014a808b --- /dev/null +++ b/gui/fitCommands/guiChangeProjectedFitQty.py @@ -0,0 +1,34 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeProjectedFitQty import FitChangeProjectedFitQty +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiChangeProjectedFitQty(wx.Command): + def __init__(self, fitID, pfitID, amount=1): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.pfitID = pfitID + self.amount = amount + self.internal_history = wx.CommandProcessor() + + def Do(self): + cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiFillWithModule.py b/gui/fitCommands/guiFillWithModule.py new file mode 100644 index 000000000..0fa962bef --- /dev/null +++ b/gui/fitCommands/guiFillWithModule.py @@ -0,0 +1,50 @@ +import wx +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitAddModule import FitAddModuleCommand +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiFillWithModuleCommand(wx.Command): + def __init__(self, fitID, itemID, position=None): + """ + Handles adding an item, usually a module, to the Fitting Window. + + :param fitID: The fit ID that we are modifying + :param itemID: The item that is to be added to the Fitting View. If this turns out to be a charge, we attempt to + set the charge on the underlying module (requires position) + :param position: Optional. The position in fit.modules that we are attempting to set the item to + """ + wx.Command.__init__(self, True, "Module Add: {}".format(itemID)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.itemID = itemID + self.internal_history = wx.CommandProcessor() + self.position = position + self.old_mod = None + + def Do(self): + pyfalog.debug("{} Do()".format(self)) + pyfalog.debug("Trying to append a module") + added_modules = 0 + success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID)) + while (success): + added_modules += 1 + success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID)) + + if added_modules > 0: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=self.itemID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=self.itemID)) + return True diff --git a/gui/fitCommands/guiFitRename.py b/gui/fitCommands/guiFitRename.py new file mode 100644 index 000000000..b9a7a81d7 --- /dev/null +++ b/gui/fitCommands/guiFitRename.py @@ -0,0 +1,30 @@ +import wx +import gui.mainFrame +from .calc.fitRename import FitRenameCommand +from service.fit import Fit +from logbook import Logger +from gui.builtinShipBrowser.events import FitRenamed +pyfalog = Logger(__name__) + + +class GuiFitRenameCommand(wx.Command): + def __init__(self, fitID, newName): + wx.Command.__init__(self, True) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.newName = newName + self.internal_history = wx.CommandProcessor() + + def Do(self): + if self.internal_history.Submit(FitRenameCommand(self.fitID, self.newName)): + wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID)) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiImportMutatedModule.py b/gui/fitCommands/guiImportMutatedModule.py new file mode 100644 index 000000000..9187adf7e --- /dev/null +++ b/gui/fitCommands/guiImportMutatedModule.py @@ -0,0 +1,38 @@ +import wx +import eos.db +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitImportMutatedModule import FitImportMutatedCommand +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiImportMutatedModuleCommand(wx.Command): + + def __init__(self, fitID, baseItem, mutaItem, attrMap): + wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem, mutaItem, attrMap)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.baseItem = baseItem + self.mutaItem = mutaItem + self.attrMap = attrMap + self.internal_history = wx.CommandProcessor() + + def Do(self): + pyfalog.debug("{} Do()".format(self)) + + if self.internal_history.Submit(FitImportMutatedCommand(self.fitID, self.baseItem, self.mutaItem, self.attrMap)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd")) + return True + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel")) + return True diff --git a/gui/fitCommands/guiMetaSwap.py b/gui/fitCommands/guiMetaSwap.py new file mode 100644 index 000000000..d0c99b8bf --- /dev/null +++ b/gui/fitCommands/guiMetaSwap.py @@ -0,0 +1,65 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveImplant import FitRemoveImplantCommand +from .calc.fitAddImplant import FitAddImplantCommand +from .calc.fitRemoveBooster import FitRemoveBoosterCommand +from .calc.fitAddBooster import FitAddBoosterCommand +from .calc.fitRemoveCargo import FitRemoveCargoCommand +from .calc.fitAddCargo import FitAddCargoCommand +from .calc.fitReplaceModule import FitReplaceModuleCommand +from .calc.fitAddFighter import FitAddFighterCommand +from .calc.fitRemoveFighter import FitRemoveFighterCommand +from .calc.fitChangeDroneVariation import FitChangeDroneVariationCommand + + +class GuiMetaSwapCommand(wx.Command): + def __init__(self, fitID, context, itemID, selection: list): + wx.Command.__init__(self, True, "Meta Swap") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + self.context = context + self.data = [] + fit = self.sFit.getFit(fitID) + + if context == 'fittingModule': + for x in selection: + self.data.append(((FitReplaceModuleCommand, fitID, fit.modules.index(x), itemID),),) + elif context == 'implantItem': + for x in selection: + idx = fit.implants.index(x) + self.data.append(((FitRemoveImplantCommand, fitID, idx), (FitAddImplantCommand, fitID, itemID))) + elif context == 'boosterItem': + for x in selection: + idx = fit.boosters.index(x) + self.data.append(((FitRemoveBoosterCommand, fitID, idx), (FitAddBoosterCommand, fitID, itemID))) + elif context == 'cargoItem': + for x in selection: + self.data.append(((FitRemoveCargoCommand, fitID, x.itemID, 1, True), (FitAddCargoCommand, fitID, itemID, x.amount))) + elif context == 'fighterItem': + for x in selection: + self.data.append(((FitRemoveFighterCommand, fitID, fit.fighters.index(x)), (FitAddFighterCommand, fitID, itemID))) + elif context == 'droneItem': + for x in selection: + self.data.append(((FitChangeDroneVariationCommand, fitID, fit.drones.index(x), itemID),),) + + def Do(self): + for cmds in self.data: + for cmd in cmds: + self.internal_history.Submit(cmd[0](*cmd[1:])) + + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiModuleToCargo.py b/gui/fitCommands/guiModuleToCargo.py new file mode 100644 index 000000000..66a9f9833 --- /dev/null +++ b/gui/fitCommands/guiModuleToCargo.py @@ -0,0 +1,73 @@ +import wx +from logbook import Logger + +import gui.mainFrame +from gui import globalEvents as GE +from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand +from gui.fitCommands.calc.fitRemoveModule import FitRemoveModuleCommand +from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand +from service.fit import Fit +from .calc.fitAddCargo import FitAddCargoCommand + +pyfalog = Logger(__name__) + + +class GuiModuleToCargoCommand(wx.Command): + def __init__(self, fitID, moduleIdx, cargoIdx, copy=False): + wx.Command.__init__(self, True, "Module State Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.moduleIdx = moduleIdx + self.cargoIdx = cargoIdx + self.copy = copy + self.internal_history = wx.CommandProcessor() + + def Do(self): + sFit = Fit.getInstance() + fit = sFit.getFit(self.fitID) + module = fit.modules[self.moduleIdx] + result = False + + if self.cargoIdx: # we're swapping with cargo + if self.copy: # if copying, simply add item to cargo + result = self.internal_history.Submit(FitAddCargoCommand( + self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID)) + else: # otherwise, try to swap by replacing module with cargo item. If successful, remove old cargo and add new cargo + + cargo = fit.cargo[self.cargoIdx] + self.modReplaceCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID) + + result = self.internal_history.Submit(self.modReplaceCmd) + + if not result: + # creating module failed for whatever reason + return False + + if self.modReplaceCmd.old_module is not None: + # we're swapping with an existing module, so remove cargo and add module + self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID) + result = self.internal_history.Submit(self.removeCmd) + + self.addCargoCmd = FitAddCargoCommand(self.fitID, self.modReplaceCmd.old_module.itemID) + result = self.internal_history.Submit(self.addCargoCmd) + + else: # dragging to blank spot, append + result = self.internal_history.Submit(FitAddCargoCommand(self.mainFrame.getActiveFit(), + module.item.ID if not module.item.isAbyssal else module.baseItemID)) + + if not self.copy: # if not copying, remove module + self.internal_history.Submit(FitRemoveModuleCommand(self.mainFrame.getActiveFit(), [self.moduleIdx])) + + if result: + sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=module.item.ID)) + + return result + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveBooster.py b/gui/fitCommands/guiRemoveBooster.py new file mode 100644 index 000000000..15b504575 --- /dev/null +++ b/gui/fitCommands/guiRemoveBooster.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveBooster import FitRemoveBoosterCommand + + +class GuiRemoveBoosterCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitRemoveBoosterCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveCargo.py b/gui/fitCommands/guiRemoveCargo.py new file mode 100644 index 000000000..f9aa5872a --- /dev/null +++ b/gui/fitCommands/guiRemoveCargo.py @@ -0,0 +1,28 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveCargo import FitRemoveCargoCommand + + +class GuiRemoveCargoCommand(wx.Command): + def __init__(self, fitID, itemID): + wx.Command.__init__(self, True, "Module Charge Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.itemID = itemID + + def Do(self): + if self.internal_history.Submit(FitRemoveCargoCommand(self.fitID, self.itemID, stack=True)): + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.GetCommands(): + self.internal_history.Undo() + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveCommand.py b/gui/fitCommands/guiRemoveCommand.py new file mode 100644 index 000000000..206be23c3 --- /dev/null +++ b/gui/fitCommands/guiRemoveCommand.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveCommand import FitRemoveCommandCommand + + +class GuiRemoveCommandCommand(wx.Command): + def __init__(self, fitID, commandFitID): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.commandFitID = commandFitID + + def Do(self): + if self.internal_history.Submit(FitRemoveCommandCommand(self.fitID, self.commandFitID)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveDrone.py b/gui/fitCommands/guiRemoveDrone.py new file mode 100644 index 000000000..42e651e3d --- /dev/null +++ b/gui/fitCommands/guiRemoveDrone.py @@ -0,0 +1,31 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveDrone import FitRemoveDroneCommand + + +class GuiRemoveDroneCommand(wx.Command): + def __init__(self, fitID, position, amount=1): + wx.Command.__init__(self, True, "Cargo Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + self.amount = amount + + def Do(self): + cmd = FitRemoveDroneCommand(self.fitID, self.position, self.amount) + if self.internal_history.Submit(cmd): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveFighter.py b/gui/fitCommands/guiRemoveFighter.py new file mode 100644 index 000000000..f1b983ec5 --- /dev/null +++ b/gui/fitCommands/guiRemoveFighter.py @@ -0,0 +1,32 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveFighter import FitRemoveFighterCommand + + +class GuiRemoveFighterCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "Module Remove") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.position = position + self.internal_history = wx.CommandProcessor() + + def Do(self): + success = self.internal_history.Submit(FitRemoveFighterCommand(self.fitID, self.position)) + + if success: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveImplant.py b/gui/fitCommands/guiRemoveImplant.py new file mode 100644 index 000000000..876421e7a --- /dev/null +++ b/gui/fitCommands/guiRemoveImplant.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveImplant import FitRemoveImplantCommand + + +class GuiRemoveImplantCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitRemoveImplantCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiRemoveModule.py b/gui/fitCommands/guiRemoveModule.py new file mode 100644 index 000000000..944a63160 --- /dev/null +++ b/gui/fitCommands/guiRemoveModule.py @@ -0,0 +1,40 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .helpers import ModuleInfoCache +from .calc.fitRemoveModule import FitRemoveModuleCommand + + +class GuiModuleRemoveCommand(wx.Command): + def __init__(self, fitID, modules): + """ + Handles removing modules from fit.modules, + + :param fitID: The fit ID that we are modifying + :param modules: A list of Module objects that we are attempting to remove. + """ + wx.Command.__init__(self, True, "Module Remove") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.modCache = [ModuleInfoCache( + mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID) for mod in modules if not mod.isEmpty] + self.internal_history = wx.CommandProcessor() + + def Do(self): + success = self.internal_history.Submit(FitRemoveModuleCommand(self.fitID, [mod.modPosition for mod in self.modCache])) + + if success: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=set([mod.itemID for mod in self.modCache]))) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=set([mod.itemID for mod in self.modCache]))) + return True diff --git a/gui/fitCommands/guiRemoveProjected.py b/gui/fitCommands/guiRemoveProjected.py new file mode 100644 index 000000000..74d1ab308 --- /dev/null +++ b/gui/fitCommands/guiRemoveProjected.py @@ -0,0 +1,89 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand +from .calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand +from .calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand +from .calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand +from logbook import Logger +from .calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand + +from eos.saveddata.drone import Drone +from eos.saveddata.module import Module +from eos.saveddata.fighter import Fighter + +pyfalog = Logger(__name__) + + +class GuiRemoveProjectedCommand(wx.Command): + mapping = { + 'fit': FitRemoveProjectedFitCommand, + 'module': FitRemoveProjectedModuleCommand, + 'fighter': FitRemoveProjectedFighterCommand, + 'env': FitRemoveProjectedEnvCommand, + 'drone': FitRemoveProjectedDroneCommand + } + + def __init__(self, fitID, thing): + wx.Command.__init__(self, True, "Projected Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + fit = self.sFit.getFit(fitID) + + if isinstance(thing, Drone): + self.data = fit.projectedDrones.index(thing) + self.type = 'drone' + elif isinstance(thing, Module): + # todo: projected stuff should be wrapped in a projected class wrapper for easier maintainence + if thing.item.group.name in Module.SYSTEM_GROUPS: + self.type = 'env' + self.data = thing.itemID + else: + self.type = 'module' + self.data = fit.projectedModules.index(thing) + elif isinstance(thing, Fighter): + self.data = fit.projectedFighters.index(thing) + self.type = 'fighter' + else: + # todo: fix! + self.data = thing.ID + self.type = 'fit' + + def Do(self): + result = False + # since we can project various types, we need to switch of the fit command. We can't do this switch easily in + # the fit command since each type might have a different kind of undo, easier to split it out + + cls = self.mapping.get(self.type, None) + if cls: + cmd = cls(self.fitID, self.data) + result = self.internal_history.Submit(cmd) + + # if item.category.name == "Drone": + # pyfalog.warn("DRONE REMOVE PROJECTION NOT IMPLEMENTED") + # elif item.category.name == "Fighter": + # pyfalog.warn("FIGHTER REMOVE PROJECTION NOT IMPLEMENTED") + # elif item.group.name in Module.SYSTEM_GROUPS: + # result = self.internal_history.Submit(FitRemoveProjectedEnvCommand(self.fitID, self.id)) + # else: + # # attempt a regular module projection + # + # elif self.type == 'fit': + # pyfalog.warn("FIT REMOVE PROJECTION NOT IMPLEMENTED") + + if result: + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiSetMode.py b/gui/fitCommands/guiSetMode.py new file mode 100644 index 000000000..9639028f9 --- /dev/null +++ b/gui/fitCommands/guiSetMode.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitSetMode import FitSetModeCommand + + +class GuiSetModeCommand(wx.Command): + def __init__(self, fitID, mode): + wx.Command.__init__(self, True, "Cargo Add") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.mode = mode + + def Do(self): + if self.internal_history.Submit(FitSetModeCommand(self.fitID, self.mode)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiSwapCloneModule.py b/gui/fitCommands/guiSwapCloneModule.py new file mode 100644 index 000000000..cbeaf0845 --- /dev/null +++ b/gui/fitCommands/guiSwapCloneModule.py @@ -0,0 +1,50 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from gui.fitCommands.calc.fitSwapModule import FitSwapModuleCommand +from .calc.fitCloneModule import FitCloneModuleCommand +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiModuleSwapOrCloneCommand(wx.Command): + + def __init__(self, fitID, srcPosition, dstPosition, clone=False): + wx.Command.__init__(self, True, "Module State Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.srcPosition = srcPosition + self.dstPosition = dstPosition + self.clone = clone + self.internal_history = wx.CommandProcessor() + + def Do(self): + pyfalog.debug("{} Do()".format(self)) + + if self.clone: + pyfalog.debug("Trying to clone module") + if self.internal_history.Submit(FitCloneModuleCommand(self.fitID, self.srcPosition, self.dstPosition)): + self.sFit.recalc(self.fitID) # clone needs a recalc + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + else: + pyfalog.debug("Trying to Swap module") + if self.internal_history.Submit(FitSwapModuleCommand(self.fitID, self.srcPosition, self.dstPosition)): + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + + return False + + def Undo(self): + pyfalog.debug("{} Undo()".format(self)) + for _ in self.internal_history.Commands: + self.internal_history.Undo() + + if self.clone: + self.sFit.recalc(self.fitID) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleBooster.py b/gui/fitCommands/guiToggleBooster.py new file mode 100644 index 000000000..2a586a888 --- /dev/null +++ b/gui/fitCommands/guiToggleBooster.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitToggleBooster import FitToggleBoosterCommand + + +class GuiToggleBoosterCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitToggleBoosterCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleCommand.py b/gui/fitCommands/guiToggleCommand.py new file mode 100644 index 000000000..50785da26 --- /dev/null +++ b/gui/fitCommands/guiToggleCommand.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitToggleCommand import FitToggleCommandCommand + + +class GuiToggleCommandCommand(wx.Command): + def __init__(self, fitID, commandFitID): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.commandFitID = commandFitID + + def Do(self): + if self.internal_history.Submit(FitToggleCommandCommand(self.fitID, self.commandFitID)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleDrone.py b/gui/fitCommands/guiToggleDrone.py new file mode 100644 index 000000000..7913df516 --- /dev/null +++ b/gui/fitCommands/guiToggleDrone.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitToggleDrone import FitToggleDroneCommand + + +class GuiToggleDroneCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitToggleDroneCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleFighter.py b/gui/fitCommands/guiToggleFighter.py new file mode 100644 index 000000000..f5aee10d6 --- /dev/null +++ b/gui/fitCommands/guiToggleFighter.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitToggleFighter import FitToggleFighterCommand + + +class GuiToggleFighterCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitToggleFighterCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleImplant.py b/gui/fitCommands/guiToggleImplant.py new file mode 100644 index 000000000..899eff0ee --- /dev/null +++ b/gui/fitCommands/guiToggleImplant.py @@ -0,0 +1,30 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitToggleImplant import FitToggleImplantCommand + + +class GuiToggleImplantCommand(wx.Command): + def __init__(self, fitID, position): + wx.Command.__init__(self, True, "") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.internal_history = wx.CommandProcessor() + self.fitID = fitID + self.position = position + + def Do(self): + if self.internal_history.Submit(FitToggleImplantCommand(self.fitID, self.position)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/guiToggleModuleState.py b/gui/fitCommands/guiToggleModuleState.py new file mode 100644 index 000000000..71e307f93 --- /dev/null +++ b/gui/fitCommands/guiToggleModuleState.py @@ -0,0 +1,33 @@ +import wx +from service.fit import Fit + +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitChangeState import FitChangeStatesCommand + + +class GuiModuleStateChangeCommand(wx.Command): + def __init__(self, fitID, baseMod, modules, click): + # todo: instead of modules, needs to be positions. Dead objects are a thing + wx.Command.__init__(self, True, "Module State Change") + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.baseMod = baseMod + self.modules = modules + self.click = click + self.internal_history = wx.CommandProcessor() + + def Do(self): + if self.internal_history.Submit(FitChangeStatesCommand(self.fitID, self.baseMod, self.modules, self.click)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True + return False + + def Undo(self): + for _ in self.internal_history.Commands: + self.internal_history.Undo() + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID)) + return True diff --git a/gui/fitCommands/helpers.py b/gui/fitCommands/helpers.py new file mode 100644 index 000000000..63aaf4b76 --- /dev/null +++ b/gui/fitCommands/helpers.py @@ -0,0 +1,3 @@ +from collections import namedtuple + +ModuleInfoCache = namedtuple('ModuleInfoCache', ['modPosition', 'itemID', 'state', 'charge', 'baseID', 'mutaplasmidID']) diff --git a/gui/itemStats.py b/gui/itemStats.py index 3440fb322..50a02f065 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -34,6 +34,9 @@ from gui.builtinItemStatsViews.itemDependants import ItemDependents from gui.builtinItemStatsViews.itemEffects import ItemEffects from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy from gui.builtinItemStatsViews.itemProperties import ItemProperties +from gui.builtinItemStatsViews.itemMutator import ItemMutator + +from eos.saveddata.module import Module class ItemStatsDialog(wx.Dialog): @@ -79,10 +82,8 @@ class ItemStatsDialog(wx.Dialog): item = sMkt.getItem(victim.ID) victim = None self.context = itmContext - if item.icon is not None: - before, sep, after = item.icon.iconFile.rpartition("_") - iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) - itemImg = BitmapLoader.getBitmap(iconFile, "icons") + if item.iconID is not None: + itemImg = BitmapLoader.getBitmap(item.iconID, "icons") if itemImg is not None: self.SetIcon(wx.Icon(itemImg)) self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, @@ -90,7 +91,7 @@ class ItemStatsDialog(wx.Dialog): self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room - self.SetSize((580, 500)) + self.SetSize((630, 550)) else: self.SetSize((550, 500)) # self.SetMaxSize((500, -1)) @@ -101,7 +102,7 @@ class ItemStatsDialog(wx.Dialog): if "wxGTK" in wx.PlatformInfo: self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0) self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) - self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) + self.closeBtn.Bind(wx.EVT_BUTTON, (lambda e: self.Close())) self.SetSizer(self.mainSizer) @@ -146,7 +147,7 @@ class ItemStatsDialog(wx.Dialog): ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) - self.Destroy() + event.Skip() class ItemStatsContainer(wx.Panel): @@ -163,8 +164,13 @@ class ItemStatsContainer(wx.Panel): self.traits = ItemTraits(self.nbContainer, stuff, item) self.nbContainer.AddPage(self.traits, "Traits") - self.desc = ItemDescription(self.nbContainer, stuff, item) - self.nbContainer.AddPage(self.desc, "Description") + if isinstance(stuff, Module) and stuff.isMutated: + self.mutator = ItemMutator(self.nbContainer, stuff, item) + self.nbContainer.AddPage(self.mutator, "Mutations") + + if item.description: + self.desc = ItemDescription(self.nbContainer, stuff, item) + self.nbContainer.AddPage(self.desc, "Description") self.params = ItemParams(self.nbContainer, stuff, item, context) self.nbContainer.AddPage(self.params, "Attributes") diff --git a/gui/mainFrame.py b/gui/mainFrame.py index c9e5fa09c..d5bc8f0e9 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -17,74 +17,63 @@ # along with pyfa. If not, see . # ============================================================================= -import sys +import datetime import os.path -from logbook import Logger - -import sqlalchemy -# noinspection PyPackageRequirements -import wx -# noinspection PyPackageRequirements -from wx.lib.wordwrap import wordwrap -# noinspection PyPackageRequirements -from wx.lib.inspection import InspectionTool +import threading import time - +import webbrowser from codecs import open - -import config - -from eos.config import gamedata_version - -import gui.aboutData -from gui.chrome_tabs import ChromeNotebook -import gui.globalEvents as GE - -from gui.bitmap_loader import BitmapLoader -from gui.mainMenuBar import MainMenuBar -from gui.additionsPane import AdditionsPane -from gui.marketBrowser import MarketBrowser -from gui.builtinMarketBrowser.events import ItemSelected -from gui.multiSwitch import MultiSwitch -from gui.statsPane import StatsPane -from gui.shipBrowser import ShipBrowser -from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected -from gui.characterEditor import CharacterEditor -from gui.characterSelection import CharacterSelection -from gui.patternEditor import DmgPatternEditorDlg -from gui.resistsEditor import ResistsEditorDlg -from gui.setEditor import ImplantSetEditorDlg -from gui.devTools import DevTools -from gui.preferenceDialog import PreferenceDialog -from gui.graphFrame import GraphFrame -from gui.ssoLogin import SsoLogin -from gui.copySelectDialog import CopySelectDialog -from gui.utils.clipboard import toClipboard, fromClipboard -from gui.updateDialog import UpdateDialog -# noinspection PyUnresolvedReferences -from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401 -from gui import graphFrame - -from service.settings import SettingsProvider -from service.fit import Fit -from service.character import Character -from service.update import Update - -# import this to access override setting -from eos.modifiedAttributeDict import ModifiedAttributeDict -from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues -from eos.db.saveddata.queries import getFit as db_getFit -from service.port import Port, IPortUser -from service.settings import HTMLExportSettings - from time import gmtime, strftime -import threading -import webbrowser +# noinspection PyPackageRequirements +import wx import wx.adv +from logbook import Logger +# noinspection PyPackageRequirements +# noinspection PyPackageRequirements +from wx.lib.inspection import InspectionTool -from service.esi import Esi, LoginMethod +import config +import gui.globalEvents as GE +from eos.config import gamedata_date, gamedata_version +from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.db.saveddata.queries import getFit as db_getFit +# import this to access override setting +from eos.modifiedAttributeDict import ModifiedAttributeDict +from gui import graphFrame +from gui.additionsPane import AdditionsPane +from gui.bitmap_loader import BitmapLoader +from gui.builtinMarketBrowser.events import ItemSelected +from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected +# noinspection PyUnresolvedReferences +from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401 +from gui.characterEditor import CharacterEditor +from gui.characterSelection import CharacterSelection +from gui.chrome_tabs import ChromeNotebook +from gui.copySelectDialog import CopySelectDialog +from gui.devTools import DevTools from gui.esiFittings import EveFittings, ExportToEve, SsoCharacterMgmt +from gui.graphFrame import GraphFrame +from gui.mainMenuBar import MainMenuBar +from gui.marketBrowser import MarketBrowser +from gui.multiSwitch import MultiSwitch +from gui.patternEditor import DmgPatternEditorDlg +from gui.preferenceDialog import PreferenceDialog +from gui.resistsEditor import ResistsEditorDlg +from gui.setEditor import ImplantSetEditorDlg +from gui.shipBrowser import ShipBrowser +from gui.ssoLogin import SsoLogin +from gui.statsPane import StatsPane +from gui.updateDialog import UpdateDialog +from gui.utils.clipboard import fromClipboard, toClipboard +from service.character import Character +from service.esi import Esi, LoginMethod +from service.esiAccess import SsoMode +from service.fit import Fit +from service.port import EfsPort, IPortUser, Port +from service.settings import HTMLExportSettings, SettingsProvider +from service.update import Update +import gui.fitCommands as cmd disableOverrideEditor = False @@ -97,6 +86,8 @@ except ImportError as e: pyfalog = Logger(__name__) +pyfalog.debug("Done loading mainframe imports") + # dummy panel(no paint no erasebk) class PFPanel(wx.Panel): @@ -150,6 +141,7 @@ class MainFrame(wx.Frame): pyfalog.debug("Initialize MainFrame") self.title = title wx.Frame.__init__(self, None, wx.ID_ANY, self.title) + self.supress_left_up = False MainFrame.__instance = self @@ -240,13 +232,17 @@ class MainFrame(wx.Frame): self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin) + @property + def command(self) -> wx.CommandProcessor: + return Fit.getCommandProcessor(self.getActiveFit()) + def ShowSsoLogin(self, event): - if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL: + if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL and getattr(event, "sso_mode", SsoMode.AUTO) == SsoMode.AUTO: dlg = SsoLogin(self) if dlg.ShowModal() == wx.ID_OK: sEsi = Esi.getInstance() # todo: verify that this is a correct SSO Info block - sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip()) + sEsi.handleLogin({'SSOInfo': [dlg.ssoInfoCtrl.Value.strip()]}) def ShowUpdateBox(self, release, version): dlg = UpdateDialog(self, release, version) @@ -364,7 +360,8 @@ class MainFrame(wx.Frame): def ShowAboutBox(self, evt): info = wx.adv.AboutDialogInfo() info.Name = "pyfa" - info.Version = config.getVersion() # gui.aboutData.versionString + time = datetime.datetime.fromtimestamp(int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S') + info.Version = config.getVersion() + '\nEVE Data Version: {} ({})'.format(gamedata_version, time) # gui.aboutData.versionString # # try: # import matplotlib @@ -427,6 +424,7 @@ class MainFrame(wx.Frame): style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: + self.supress_left_up = True format_ = dlg.GetFilterIndex() path = dlg.GetPath() if format_ == 0: @@ -509,6 +507,10 @@ class MainFrame(wx.Frame): self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) + + self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Undo(), id=wx.ID_UNDO) + + self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Redo(), id=wx.ID_REDO) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) # Save current character @@ -697,51 +699,63 @@ class MainFrame(wx.Frame): else: self.marketBrowser.search.Focus() - def clipboardEft(self): + def clipboardEft(self, options): fit = db_getFit(self.getActiveFit()) - toClipboard(Port.exportEft(fit)) + toClipboard(Port.exportEft(fit, options)) - def clipboardEftImps(self): - fit = db_getFit(self.getActiveFit()) - toClipboard(Port.exportEftImps(fit)) - - def clipboardDna(self): + def clipboardDna(self, options): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportDna(fit)) - def clipboardEsi(self): + def clipboardEsi(self, options): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportESI(fit)) - def clipboardXml(self): + def clipboardXml(self, options): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportXml(None, fit)) - def clipboardMultiBuy(self): + def clipboardMultiBuy(self, options): fit = db_getFit(self.getActiveFit()) - toClipboard(Port.exportMultiBuy(fit)) + toClipboard(Port.exportMultiBuy(fit, options)) + + def clipboardEfs(self, options): + fit = db_getFit(self.getActiveFit()) + toClipboard(EfsPort.exportEfs(fit, 0)) def importFromClipboard(self, event): clipboard = fromClipboard() + activeFit = self.getActiveFit() try: - fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) + importType, importData = Port().importFitFromBuffer(clipboard, activeFit) + # If it's mutated item - make sure there's at least base item specified + if importType == "MutatedItem": + # we've imported an Abyssal module, need to fire off the command to add it to the fit + self.command.Submit(cmd.GuiImportMutatedModuleCommand(activeFit, *importData[0])) + return # no need to do anything else except: pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: - self._openAfterImport(fits) + self._openAfterImport(importData) def exportToClipboard(self, event): CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft, - CopySelectDialog.copyFormatEftImps: self.clipboardEftImps, CopySelectDialog.copyFormatXml: self.clipboardXml, CopySelectDialog.copyFormatDna: self.clipboardDna, CopySelectDialog.copyFormatEsi: self.clipboardEsi, - CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy} + CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy, + CopySelectDialog.copyFormatEfs: self.clipboardEfs} dlg = CopySelectDialog(self) - dlg.ShowModal() - selected = dlg.GetSelected() + btnPressed = dlg.ShowModal() - CopySelectDict[selected]() + if btnPressed == wx.ID_OK: + selected = dlg.GetSelected() + options = dlg.GetOptions() + + settings = SettingsProvider.getInstance().getSettings("pyfaExport") + settings["format"] = selected + settings["options"] = options + CopySelectDict[selected](options.get(selected)) try: dlg.Destroy() @@ -966,6 +980,7 @@ class MainFrame(wx.Frame): ) if dlg.ShowModal() == wx.ID_OK: + self.supress_left_up = True self.waitDialog = wx.BusyInfo("Importing Character...") sCharacter = Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 9c421f779..c0a28f1dc 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -90,8 +90,8 @@ class MainMenuBar(wx.MenuBar): editMenu = wx.Menu() self.Append(editMenu, "&Edit") - # editMenu.Append(wx.ID_UNDO) - # editMenu.Append(wx.ID_REDO) + editMenu.Append(wx.ID_UNDO) + editMenu.Append(wx.ID_REDO) editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard") editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard") diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index 0144f651c..c3e3e1c16 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -14,11 +14,10 @@ # =============================================================================== import copy + import wx -from gui.utils import color as color_utils -from gui.utils import draw, anim_effects -from service.fit import Fit +from gui.utils import anim_effects, color as color_utils, draw class PyGauge(wx.Window): @@ -130,8 +129,8 @@ class PyGauge(wx.Window): return self._max_range def Animate(self): - sFit = Fit.getInstance() - if sFit.serviceFittingOptions["enableGaugeAnimation"]: + # sFit = Fit.getInstance() + if True: if not self._timer: self._timer = wx.Timer(self, self._timer_id) @@ -425,6 +424,13 @@ if __name__ == "__main__": gauge.SetFractionDigits(1) box.Add(gauge, 0, wx.ALL, 2) + gauge = PyGauge(self, font, size=(100, 5)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(255, 128, 0)) + gauge.SetValue(59) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL, 2) + self.SetSizer(box) self.Layout() diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 2b12edd8c..75152a1b4 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -189,7 +189,7 @@ class ShipBrowser(wx.Panel): "amarr", "caldari", "gallente", "minmatar", "sisters", "ore", "serpentis", "angel", "blood", "sansha", "guristas", "mordu", - "jove", "upwell", None + "jove", "upwell", "triglavian", None ] def raceNameKey(self, ship): @@ -236,10 +236,10 @@ class ShipBrowser(wx.Panel): if self.filterShipsWithNoFits: if fits > 0: if filter_: - self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) + self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID)) else: if filter_: - self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) + self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID)) self.raceselect.RebuildRaces(racesList) @@ -335,8 +335,8 @@ class ShipBrowser(wx.Panel): shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - for ID, name, booster, timestamp, notes in fitList: - self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID)) + for ID, name, booster, timestamp, notes, graphicID in fitList: + self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID, graphicID=graphicID)) self.lpane.RefreshList() self.lpane.Thaw() @@ -374,7 +374,7 @@ class ShipBrowser(wx.Panel): self.lpane.AddWidget( ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), - ship.race)) + ship.race, ship.graphicID)) for ID, name, shipID, shipName, booster, timestamp, notes in fitList: ship = sMkt.getItem(shipID) @@ -384,7 +384,7 @@ class ShipBrowser(wx.Panel): shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID)) + self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID, graphicID=ship.graphicID)) if len(ships) == 0 and len(fitList) == 0: self.lpane.AddWidget(PFStaticText(self.lpane, label="No matching results.")) self.lpane.RefreshList(doFocus=False) @@ -435,6 +435,7 @@ class ShipBrowser(wx.Panel): fit[4] ), shipItem.ID, + graphicID=shipItem.graphicID )) self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py index 2ff364752..4a8f1197b 100644 --- a/gui/ssoLogin.py +++ b/gui/ssoLogin.py @@ -1,5 +1,6 @@ import wx + class SsoLogin(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240)) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index c575eeaf3..c596d553e 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -62,7 +62,6 @@ class UpdateDialog(wx.Dialog): self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) link_patterns = [ - (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), (re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), (re.compile("@(\w+)", re.I), r"https://github.com/\1") ] @@ -71,12 +70,27 @@ class UpdateDialog(wx.Dialog): extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], link_patterns=link_patterns) + release_markup = markdowner.convert(self.releaseInfo['body']) + + # run the text through markup again, this time with the hashing pattern. This is required due to bugs in markdown2: + # https://github.com/trentm/python-markdown2/issues/287 + link_patterns = [ + (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), + ] + + markdowner = markdown2.Markdown( + extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], + link_patterns=link_patterns) + + # The space here is required, again, due to bug. Again, see https://github.com/trentm/python-markdown2/issues/287 + release_markup = markdowner.convert(' ' + release_markup) + self.browser.SetPage(html_tmpl.format( self.releaseInfo['tag_name'], releaseDate.strftime('%B %d, %Y'), "

This is a pre-release, be prepared for unstable features

" if version.is_prerelease else "", - markdowner.convert(self.releaseInfo['body']) - ),"") + release_markup + ), "") notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 612df367e..d1c0af9d0 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -86,11 +86,11 @@ class exportHtmlThread(threading.Thread): Pyfa Fittings - +