Compare commits
207 Commits
v2.34.0dev
...
v2.39.0dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1019c5f0cd | ||
|
|
b1b4284b6e | ||
|
|
e0377a084f | ||
|
|
dbf9875d25 | ||
|
|
5d66454e52 | ||
|
|
f5d18fd606 | ||
|
|
145e92b458 | ||
|
|
ca8a8f0785 | ||
|
|
dfdcf0a804 | ||
|
|
087284e048 | ||
|
|
65741ed03b | ||
|
|
ae3d7ade0f | ||
|
|
789df97017 | ||
|
|
7992ba43b2 | ||
|
|
3595e24ad8 | ||
|
|
6677435474 | ||
|
|
1561d9f581 | ||
|
|
1dee8ae648 | ||
|
|
51ea0c4f31 | ||
|
|
9d8e338136 | ||
|
|
48818c9709 | ||
|
|
ca356b413b | ||
|
|
a9ef5caf2d | ||
|
|
22be97f901 | ||
|
|
06f515ec0b | ||
|
|
c878f4c155 | ||
|
|
5cf433a3eb | ||
|
|
15191e5752 | ||
|
|
9d833d9cac | ||
|
|
5767c92985 | ||
|
|
3246e58278 | ||
|
|
30546c6e45 | ||
|
|
37fe587d90 | ||
|
|
09b1b50257 | ||
|
|
91a507eadd | ||
|
|
dd92c3742c | ||
|
|
181170d7b0 | ||
|
|
1156f06db2 | ||
|
|
90baffe267 | ||
|
|
100a1fa01a | ||
|
|
d8d854da52 | ||
|
|
418339d000 | ||
|
|
24a0f4f53e | ||
|
|
94784bf1e0 | ||
|
|
d894df9819 | ||
|
|
a5e1f3be40 | ||
|
|
dcb6439444 | ||
|
|
98fa16d921 | ||
|
|
d5795ed5f5 | ||
|
|
fe809ed86b | ||
|
|
d09db832c3 | ||
|
|
de4466b037 | ||
|
|
8ebf478cf7 | ||
|
|
530a12ea4f | ||
|
|
b94f6e8d0a | ||
|
|
4b83169070 | ||
|
|
a7da9e20a5 | ||
|
|
0020dcdf0a | ||
|
|
066f697186 | ||
|
|
b2217bb6cd | ||
|
|
4669b4813c | ||
|
|
a71701932c | ||
|
|
1be0d306bb | ||
|
|
85f63e237d | ||
|
|
acab787a7b | ||
|
|
da50c7dcfc | ||
|
|
b0d3469f01 | ||
|
|
148093f6ad | ||
|
|
fab58bdb57 | ||
|
|
34b2fdbd1b | ||
|
|
ea7a5b3c70 | ||
|
|
056685ded5 | ||
|
|
60f9e96b19 | ||
|
|
341950cb42 | ||
|
|
641e982614 | ||
|
|
1e534e087a | ||
|
|
0b7b80d0f6 | ||
|
|
27bd9fc55b | ||
|
|
533f6f3b24 | ||
|
|
c18cbe5e59 | ||
|
|
adba55eb7d | ||
|
|
03c6f7c894 | ||
|
|
478b2bfe4c | ||
|
|
7e4d8a3074 | ||
|
|
8497d1f1bb | ||
|
|
1b5acc36d3 | ||
|
|
2a94dcebf8 | ||
|
|
7b9e196ca8 | ||
|
|
f3f7d688ab | ||
|
|
17391f119c | ||
|
|
abd138a015 | ||
|
|
33aa208513 | ||
|
|
1874cbe0c5 | ||
|
|
028fb42e18 | ||
|
|
6b444bfc63 | ||
|
|
1753d79bc1 | ||
|
|
d9b0d2e72a | ||
|
|
e0234c343e | ||
|
|
b5816a312c | ||
|
|
86cc025812 | ||
|
|
8072f7e618 | ||
|
|
c34b12d806 | ||
|
|
67760fa2ab | ||
|
|
9a28dd17be | ||
|
|
6e4d1876ad | ||
|
|
40b0b27176 | ||
|
|
b238422f29 | ||
|
|
4e20583b81 | ||
|
|
f730cdb86a | ||
|
|
e05471fa6f | ||
|
|
45b517e097 | ||
|
|
f27274c9e5 | ||
|
|
d32a8f0b61 | ||
|
|
6a01086cf5 | ||
|
|
e0f65c2370 | ||
|
|
e7c2f4cff7 | ||
|
|
93cce50d00 | ||
|
|
d1a2cdc586 | ||
|
|
9dab6b0f92 | ||
|
|
33e3ea6030 | ||
|
|
9d97b27c3a | ||
|
|
05d156c7f4 | ||
|
|
2ddc4924cc | ||
|
|
e3fef6f1a7 | ||
|
|
fbd8d04f68 | ||
|
|
23916165bb | ||
|
|
3cca6a6c95 | ||
|
|
a52172a3c6 | ||
|
|
8a2bfb48ce | ||
|
|
b42254cb61 | ||
|
|
91fbcd4eb4 | ||
|
|
bdc52da05d | ||
|
|
2bbe17722b | ||
|
|
3ef07b9153 | ||
|
|
5af904ca18 | ||
|
|
d3cdc67472 | ||
|
|
0991124d5f | ||
|
|
83ea548c4d | ||
|
|
4752eee09d | ||
|
|
53d2d1fabe | ||
|
|
f600868eb2 | ||
|
|
f6a489fa22 | ||
|
|
2962a89919 | ||
|
|
c3d53f56a9 | ||
|
|
3257abbeff | ||
|
|
5858d96deb | ||
|
|
94d1eae464 | ||
|
|
b7e2b782c9 | ||
|
|
05e5958d36 | ||
|
|
e9364fe65a | ||
|
|
b3b71896ae | ||
|
|
00d20f53d0 | ||
|
|
88e4f7a77e | ||
|
|
5763954f51 | ||
|
|
4fbfbcc84e | ||
|
|
1f2c20e95b | ||
|
|
d7aa744bc1 | ||
|
|
1117c786ff | ||
|
|
6a715f6678 | ||
|
|
5e93235b02 | ||
|
|
271b915ce6 | ||
|
|
52667da64a | ||
|
|
82f0fea1bf | ||
|
|
751823177b | ||
|
|
ac7e8a1efa | ||
|
|
066a12e912 | ||
|
|
ea64657fba | ||
|
|
17b94001e0 | ||
|
|
cc481f9f29 | ||
|
|
f73bb14990 | ||
|
|
a5ed992cd2 | ||
|
|
f7e792411a | ||
|
|
fc71d7b706 | ||
|
|
162055d5ce | ||
|
|
5cc87a439f | ||
|
|
d2d3a42adf | ||
|
|
e43cb74906 | ||
|
|
ace00d495a | ||
|
|
729b39e351 | ||
|
|
5ae1e64a76 | ||
|
|
88a9ce03ba | ||
|
|
cbd3d8cc1b | ||
|
|
65a126b4c5 | ||
|
|
241c744d40 | ||
|
|
8ee900a90e | ||
|
|
1c415fbe06 | ||
|
|
304aebbccf | ||
|
|
18d9f6a542 | ||
|
|
cdc6e046b0 | ||
|
|
59bb9670b6 | ||
|
|
9321a78a61 | ||
|
|
53191714f2 | ||
|
|
b17347156c | ||
|
|
66ace06194 | ||
|
|
e8697c6131 | ||
|
|
7a6e87330d | ||
|
|
2acf92160e | ||
|
|
6e49f6fd7a | ||
|
|
6e56230a0e | ||
|
|
01282d103a | ||
|
|
7b3bda5816 | ||
|
|
f62e16288d | ||
|
|
9aad8860a9 | ||
|
|
f8dd1f27a9 | ||
|
|
f531c144af | ||
|
|
dfd08fa8e2 | ||
|
|
e430848be9 |
205
.appveyor.yml
@@ -1,96 +1,129 @@
|
||||
image: Visual Studio 2019
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
- macos
|
||||
clone_depth: 400
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- PYTHON: "C:\\Python37-x64"
|
||||
# Should be enabled only for build process debugging
|
||||
# init:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
install:
|
||||
- ps: echo("OS version:")
|
||||
- ps: "[System.Environment]::OSVersion.Version"
|
||||
for:
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Visual Studio 2019
|
||||
environment:
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
# Should be enabled only for build process debugging
|
||||
# init:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
install:
|
||||
- ps: echo("OS version:")
|
||||
- ps: "[System.Environment]::OSVersion.Version"
|
||||
|
||||
- ps: echo("Filesystem - root:")
|
||||
- ps: "ls \"C:\\\""
|
||||
- ps: echo("Filesystem - root:")
|
||||
- ps: "ls \"C:\\\""
|
||||
|
||||
- ps: echo("Filesystem - projects root:")
|
||||
- ps: "ls \"C:\\projects\\\""
|
||||
- ps: echo("Filesystem - projects root:")
|
||||
- ps: "ls \"C:\\projects\\\""
|
||||
|
||||
- ps: echo("Filesystem - pyfa root:")
|
||||
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
|
||||
- ps: echo("Filesystem - pyfa root:")
|
||||
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
|
||||
|
||||
- ps: echo("Filesystem - installed SDKs:")
|
||||
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
|
||||
- ps: echo("Filesystem - installed SDKs:")
|
||||
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
|
||||
|
||||
# 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).
|
||||
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
- cmd: "appveyor DownloadFile https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.20.2-v1.16/gettext0.20.2-iconv1.16-shared-64.zip"
|
||||
- cmd: "7z x gettext0.20.2-iconv1.16-shared-64.zip -ogettext"
|
||||
- cmd: "SET PATH=gettext;%PATH%"
|
||||
# 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).
|
||||
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
- cmd: "appveyor DownloadFile https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.20.2-v1.16/gettext0.20.2-iconv1.16-shared-64.zip"
|
||||
- cmd: "7z x gettext0.20.2-iconv1.16-shared-64.zip -ogettext"
|
||||
- cmd: "SET PATH=gettext;%PATH%"
|
||||
|
||||
- cmd: "python --version"
|
||||
- cmd: "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
- cmd: "python --version"
|
||||
- cmd: "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- cmd: "python -m pip install --upgrade pip"
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- cmd: "python -m pip install --upgrade pip"
|
||||
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# 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
|
||||
- ps: echo("Install pip requirements:")
|
||||
# This one is needed to build wxpython 4.0.6 on windows
|
||||
- cmd: "python -m pip install pathlib2"
|
||||
- cmd: "python -m pip install -r requirements.txt"
|
||||
- cmd: "python -m pip install PyInstaller==3.6"
|
||||
|
||||
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 $env:PYFA_VERSION")
|
||||
|
||||
build_script:
|
||||
- ps: echo("Build pyfa:")
|
||||
- ps: Get-ChildItem locale/*.po -Recurse -File| Foreach {msgen $_.fullname -o $_.fullname}
|
||||
# Build language files
|
||||
- cmd: "python scripts/compile_lang.py"
|
||||
# Dump language progress
|
||||
- cmd: "python scripts/dump_crowdin_progress.py"
|
||||
# Build gamedata DB
|
||||
- cmd: "python db_update.py"
|
||||
# Build command for PyInstaller
|
||||
- cmd: "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\
|
||||
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
|
||||
- cmd: "python dist_assets/win/dist.py"
|
||||
- ps: dir $env:PYFA_DIST_DIR/
|
||||
|
||||
after_build:
|
||||
- ps: "ls \"./\""
|
||||
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*"
|
||||
|
||||
test_script:
|
||||
# Ha... we're just building
|
||||
|
||||
artifacts:
|
||||
- path: pyfa*-win.zip
|
||||
- path: pyfa*-win.exe
|
||||
|
||||
deploy:
|
||||
tag: $(pyfa_version)
|
||||
release: pyfa $(pyfa_version)
|
||||
description: 'Release description'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: X+U3hOAMTt7HGXCR/LXaGNF6qyhUXetrjz5+xlWiNJQ3XEdzhZZmHK75m0Hm6qre
|
||||
draft: true
|
||||
force_update: false
|
||||
# deploy on tag push only
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# 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
|
||||
- ps: echo("Install pip requirements:")
|
||||
# This one is needed to build wxpython 4.0.6 on windows
|
||||
- cmd: "python -m pip install pathlib2"
|
||||
- cmd: "python -m pip install -r requirements.txt"
|
||||
- cmd: "python -m pip install PyInstaller==3.6"
|
||||
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 $env:PYFA_VERSION")
|
||||
build_script:
|
||||
- ps: echo("Build pyfa:")
|
||||
- ps: Get-ChildItem locale/*.po -Recurse -File| Foreach {msgen $_.fullname -o $_.fullname}
|
||||
# Build language files
|
||||
- cmd: "python scripts/compile_lang.py"
|
||||
# Dump language progress
|
||||
- cmd: "python scripts/dump_crowdin_progress.py"
|
||||
# Build gamedata DB
|
||||
- cmd: "python db_update.py"
|
||||
# Build command for PyInstaller
|
||||
- cmd: "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\
|
||||
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
|
||||
- cmd: "python dist_assets/win/dist.py"
|
||||
- ps: dir $env:PYFA_DIST_DIR/
|
||||
after_build:
|
||||
- ps: "ls \"./\""
|
||||
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*"
|
||||
artifacts:
|
||||
- path: pyfa*-win.zip
|
||||
- path: pyfa*-win.exe
|
||||
deploy:
|
||||
tag: $(pyfa_version)
|
||||
release: pyfa $(pyfa_version)
|
||||
description: 'Release description'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
|
||||
draft: true
|
||||
force_update: false
|
||||
# deploy on tag push only
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: macos
|
||||
# Should be enabled only for build process debugging
|
||||
init:
|
||||
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
||||
- sh: source ~/venv3.7/bin/activate
|
||||
install:
|
||||
- sh: bash scripts/osx-setup.sh
|
||||
build_script:
|
||||
- sh: bash scripts/osx-translations.sh
|
||||
- sh: python3 scripts/compile_lang.py
|
||||
- sh: python3 scripts/dump_crowdin_progress.py
|
||||
- sh: python3 db_update.py
|
||||
after_build:
|
||||
- sh: export PYFA_VERSION="$(python3 scripts/dump_version.py)"
|
||||
- sh: bash scripts/osx-package.sh
|
||||
artifacts:
|
||||
- path: dist/pyfa*-mac.zip
|
||||
before_deploy:
|
||||
- sh: export RELEASE_PKG_FILE=$(ls *.deb)
|
||||
- sh: echo "deploying $RELEASE_PKG_FILE to GitHub releases"
|
||||
deploy:
|
||||
tag: $PYFA_VERSION
|
||||
release: pyfa $PYFA_VERSION
|
||||
description: 'Release description'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: M94o0xMtzxrvlKpqMcXU2KfbJdd3aYJ3UxWzePUz/pkT1/Ojiis052CiLsLVyzJg
|
||||
draft: true
|
||||
force_update: false
|
||||
# deploy on tag push only
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
||||
36
.travis.yml
@@ -1,36 +0,0 @@
|
||||
if:
|
||||
tags: true
|
||||
os: linux
|
||||
language: python
|
||||
git:
|
||||
depth: 400
|
||||
python:
|
||||
- 3.8
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode11.3
|
||||
language: generic
|
||||
before_install:
|
||||
- bash scripts/osx-translations.sh
|
||||
- bash scripts/osx-setup.sh
|
||||
install:
|
||||
- python3 scripts/compile_lang.py
|
||||
- python3 scripts/dump_crowdin_progress.py
|
||||
- python3 db_update.py
|
||||
- export PYFA_VERSION="$(python3 scripts/dump_version.py)"
|
||||
- bash scripts/osx-package.sh
|
||||
before_deploy:
|
||||
- export RELEASE_PKG_FILE=$(ls *.deb)
|
||||
- echo "deploying $RELEASE_PKG_FILE to GitHub releases"
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: D8tBW0kyHlKf/sXS69aIuexsYTx9auY2DzudKFlfcvdzqat4N2XZqZbZCTVd7YVvptQ8Dj0oZ/p3KUxEGpnJZmlTeJL142rpM/qaNd6wOIMy2yUde/aZl+W9JLFNQp7KHutM+MxObYLzJGihx/8YsupmFx6lxgdngGDXtXYZe/ruDIWDs92ShoKJ4vlce9Csm7eGKv7wv6Z6V9sD5FS3E9J8xdWStHxsbrkPBOflmG+uHU09dpEqzUm+ZYROIoTwig1Xbw3fw+gfjmNrfdSU4fAJcVZI1hrgoenZyJbMfhI2Ej/nZdbZgaXcZNF/eUpqOGgbPe1JljqFnHTbexcE+LPBVyAToScsGMpByHhig67DrZ0nk9gSZoC6CPNl5YS6xub+5dncMJ3P5L03DOGYRu4SL9NczbeuQyKuea7+JPP/8VLwfFDSEqbNEAmgzABAzrdfano+VXtuBuE/Tiy5eE7le9hJu6aSQoKW1SA3cUhMsmr2amzdO96sh+PN8FA1oNr45Yuy0pqOj4SUIkb8JUy4th7vgdhljEkSxrHDK1UcHpxUTp+IIUZkZVVk50aH68dQZxGwSTVOeRxpjrTcEf7VCGaM98qxi/ZK4RW6Ewiq0eo0AxwEeB2Zm841lycGPR/406vM9/ZBzv5IhELIdDdVWTk+dGjJBXB8z5hPJOg=
|
||||
file_glob: true
|
||||
file: "dist/pyfa-*.zip"
|
||||
skip_cleanup: true
|
||||
draft: true
|
||||
on:
|
||||
tags: true
|
||||
repo: pyfa-org/Pyfa
|
||||
@@ -43,7 +43,9 @@ experimentalFeatures = None
|
||||
version = None
|
||||
language = None
|
||||
|
||||
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
|
||||
ESI_CACHE = 'esi_cache'
|
||||
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
|
||||
|
||||
LOGLEVEL_MAP = {
|
||||
"critical": CRITICAL,
|
||||
|
||||
@@ -27,6 +27,8 @@ import re
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
from sqlalchemy import or_
|
||||
|
||||
|
||||
# todo: need to set the EOS language to en, becasuse this assumes it's being run within an English context
|
||||
# Need to know what that would do if called from pyfa
|
||||
@@ -148,6 +150,7 @@ def update_db():
|
||||
"'Needlejack'" not in row['typeName_en-us'] and
|
||||
"'Devana'" not in row['typeName_en-us'] and
|
||||
"'Pochven'" not in row['typeName_en-us'] and
|
||||
"'Extraction'" not in row['typeName_en-us'] and
|
||||
"'Krai Veles'" not in row['typeName_en-us'] and
|
||||
"'Krai Perun'" not in row['typeName_en-us'] and
|
||||
"'Krai Svarog'" not in row['typeName_en-us']
|
||||
@@ -590,7 +593,10 @@ def update_db():
|
||||
# pyfa, we can do it here as a post-processing step
|
||||
for attr in eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(eos.gamedata.Attribute.ID == 1367).all():
|
||||
attr.value = 4.0
|
||||
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.name.like('%abyssal%')).all():
|
||||
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(or_(
|
||||
eos.gamedata.Item.name.like('%abyssal%'),
|
||||
eos.gamedata.Item.name.like('%mutated%')
|
||||
)).all():
|
||||
item.published = False
|
||||
|
||||
for x in [
|
||||
|
||||
@@ -118,7 +118,7 @@ from eos.db.gamedata import alphaClones, attribute, category, effect, group, ite
|
||||
pyfalog.debug('Importing saveddata DB scheme')
|
||||
# noinspection PyPep8
|
||||
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
|
||||
miscData, mutator, module, override, price, queries, skill, targetProfile, user
|
||||
miscData, mutatorMod, mutatorDrone, module, override, price, queries, skill, targetProfile, user
|
||||
|
||||
pyfalog.debug('Importing gamedata queries')
|
||||
# noinspection PyPep8
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
|
||||
18
eos/db/migrations/upgrade45.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Migration 45
|
||||
|
||||
- Drone mutaplasmid support
|
||||
"""
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT baseItemID FROM drones LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN baseItemID INTEGER;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT mutaplasmidID FROM drones LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN mutaplasmidID INTEGER;")
|
||||
@@ -1,7 +1,8 @@
|
||||
__all__ = [
|
||||
"character",
|
||||
"fit",
|
||||
"mutator",
|
||||
"mutatorMod",
|
||||
"mutatorDrone",
|
||||
"module",
|
||||
"user",
|
||||
"skill",
|
||||
|
||||
@@ -24,11 +24,12 @@ import datetime
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.boosterSideEffect import BoosterSideEffect
|
||||
from eos.saveddata.fit import Fit
|
||||
|
||||
boosters_table = Table("boosters", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("itemID", Integer),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
|
||||
Column("active", Boolean),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
@@ -44,6 +45,7 @@ booster_side_effect_table = Table("boosterSideEffects", saveddata_meta,
|
||||
|
||||
mapper(Booster, boosters_table,
|
||||
properties={
|
||||
"owner": relation(Fit),
|
||||
"_Booster__sideEffects": relation(
|
||||
BoosterSideEffect,
|
||||
backref="booster",
|
||||
|
||||
@@ -18,27 +18,35 @@
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
from sqlalchemy.orm import mapper, relation, synonym
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
import datetime
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
|
||||
drones_table = Table("drones", saveddata_meta,
|
||||
Column("groupID", Integer, primary_key=True),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
|
||||
Column("itemID", Integer, nullable=False),
|
||||
Column("baseItemID", Integer, nullable=True),
|
||||
Column("mutaplasmidID", Integer, nullable=True),
|
||||
Column("amount", Integer, nullable=False),
|
||||
Column("amountActive", Integer, nullable=False),
|
||||
Column("projected", Boolean, default=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("projectionRange", Float, nullable=True)
|
||||
)
|
||||
Column("projectionRange", Float, nullable=True))
|
||||
|
||||
|
||||
mapper(Drone, drones_table,
|
||||
properties={
|
||||
"owner": relation(Fit)
|
||||
}
|
||||
)
|
||||
"ID": synonym("groupID"),
|
||||
"owner": relation(Fit),
|
||||
"mutators": relation(
|
||||
MutatorDrone,
|
||||
backref="item",
|
||||
cascade="all,delete-orphan",
|
||||
collection_class=attribute_mapped_collection('attrID'))})
|
||||
|
||||
@@ -191,7 +191,6 @@ mapper(es_Fit, fits_table,
|
||||
Booster,
|
||||
collection_class=HandledBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
backref='owner',
|
||||
single_parent=True),
|
||||
"_Fit__drones": relation(
|
||||
Drone,
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
# ===============================================================================
|
||||
|
||||
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
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
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
|
||||
from eos.saveddata.mutator import MutatorModule
|
||||
|
||||
modules_table = Table("modules", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
@@ -45,13 +45,12 @@ modules_table = Table("modules", saveddata_meta,
|
||||
Column("projectionRange", Float, nullable=True),
|
||||
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
|
||||
|
||||
|
||||
mapper(Module, modules_table,
|
||||
properties={
|
||||
"owner": relation(Fit),
|
||||
"mutators": relation(
|
||||
Mutator,
|
||||
backref="module",
|
||||
MutatorModule,
|
||||
backref="item",
|
||||
cascade="all,delete-orphan",
|
||||
collection_class=attribute_mapped_collection('attrID')
|
||||
)
|
||||
})
|
||||
collection_class=attribute_mapped_collection('attrID'))})
|
||||
|
||||
@@ -23,13 +23,14 @@ 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
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
|
||||
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))
|
||||
mutatorDrones_table = Table(
|
||||
"mutatorsDrones", saveddata_meta,
|
||||
Column("groupID", Integer, ForeignKey("drones.groupID"), 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)
|
||||
mapper(MutatorDrone, mutatorDrones_table)
|
||||
36
eos/db/saveddata/mutatorMod.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
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 MutatorModule
|
||||
|
||||
mutatorMods_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(MutatorModule, mutatorMods_table)
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm.attributes import flag_dirty
|
||||
from sqlalchemy.orm.collections import collection
|
||||
|
||||
|
||||
@@ -108,10 +109,7 @@ class HandledList(list):
|
||||
|
||||
def remove(self, thing):
|
||||
# We must flag it as modified, otherwise it not be removed from the database
|
||||
# @todo: flag_modified isn't in os x skel. need to rebuild to include
|
||||
# flag_modified(thing, "itemID")
|
||||
if thing.isInvalid: # see GH issue #324
|
||||
thing.itemID = 0
|
||||
flag_dirty(thing)
|
||||
list.remove(self, thing)
|
||||
|
||||
def sort(self, *args, **kwargs):
|
||||
|
||||
2442
eos/effects.py
@@ -567,7 +567,18 @@ class Group(EqBase):
|
||||
|
||||
|
||||
class DynamicItem(EqBase):
|
||||
pass
|
||||
|
||||
@property
|
||||
def shortName(self):
|
||||
name = self.item.customName
|
||||
keywords = ('Decayed', 'Gravid', 'Unstable', 'Radical')
|
||||
for kw in keywords:
|
||||
if name.startswith(f'{kw} '):
|
||||
name = kw
|
||||
m = re.match('(?P<mutagrade>\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\S+) Mutaplasmid', name)
|
||||
if m:
|
||||
name = '{} {}'.format(m.group('mutagrade'), m.group('mutatype'))
|
||||
return name
|
||||
|
||||
|
||||
class DynamicItemAttribute(EqBase):
|
||||
|
||||
@@ -71,30 +71,30 @@ class ItemAttrShortcut:
|
||||
|
||||
def getModifiedItemAttr(self, key, default=0):
|
||||
return_value = self.itemModifiedAttributes.get(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getModifiedItemAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
|
||||
return_value = self.itemModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getItemBaseAttrValue(self, key, default=0):
|
||||
return_value = self.itemModifiedAttributes.getOriginal(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
|
||||
class ChargeAttrShortcut:
|
||||
|
||||
def getModifiedChargeAttr(self, key, default=0):
|
||||
return_value = self.chargeModifiedAttributes.get(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getModifiedChargeAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0):
|
||||
return_value = self.chargeModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
def getChargeBaseAttrValue(self, key, default=0):
|
||||
return_value = self.chargeModifiedAttributes.getOriginal(key)
|
||||
return return_value or default
|
||||
return return_value if return_value is not None else default
|
||||
|
||||
|
||||
class ModifiedAttributeDict(MutableMapping):
|
||||
|
||||
@@ -159,6 +159,6 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
sideEffect.active = sideEffectStates[sideEffect.effectID]
|
||||
|
||||
def __repr__(self):
|
||||
return "Booster(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
if self.item is not None:
|
||||
return f"Booster(ID={self.item.ID}, name={self.item.name}) at {hex(id(self))}"
|
||||
return f"Booster(ID={self.itemID}) at {hex(id(self))}"
|
||||
|
||||
@@ -25,6 +25,8 @@ from sqlalchemy.orm import reconstructor, validates
|
||||
import eos.db
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
|
||||
from eos.saveddata.mutator import MutatorDrone
|
||||
from eos.utils.cycles import CycleInfo
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
@@ -33,12 +35,13 @@ from eos.utils.stats import DmgTypes, RRTypes
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
|
||||
MINING_ATTRIBUTES = ("miningAmount",)
|
||||
|
||||
def __init__(self, item):
|
||||
def __init__(self, item, baseItem=None, mutaplasmid=None):
|
||||
"""Initialize a drone from the program"""
|
||||
self.__item = item
|
||||
self._item = item
|
||||
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
|
||||
|
||||
if self.isInvalid:
|
||||
raise ValueError("Passed item is not a Drone")
|
||||
@@ -53,14 +56,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a drone from the database and validate"""
|
||||
self.__item = None
|
||||
self._item = None
|
||||
|
||||
if self.itemID:
|
||||
self.__item = eos.db.getItem(self.itemID)
|
||||
if self.__item is None:
|
||||
self._item = eos.db.getItem(self.itemID)
|
||||
if self._item is None:
|
||||
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
|
||||
return
|
||||
|
||||
try:
|
||||
self._mutaReconstruct()
|
||||
except MutaError:
|
||||
return
|
||||
|
||||
if self.isInvalid:
|
||||
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
|
||||
return
|
||||
@@ -72,15 +80,18 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
|
||||
self.__itemModifiedAttributes.original = self._item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self._item.overrides
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
|
||||
|
||||
self._mutaLoadMutators(mutatorClass=MutatorDrone)
|
||||
self.__itemModifiedAttributes.mutators = self.mutators
|
||||
|
||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
|
||||
if chargeID is not None:
|
||||
if chargeID:
|
||||
charge = eos.db.getItem(int(chargeID))
|
||||
self.__charge = charge
|
||||
self.__chargeModifiedAttributes.original = charge.attributes
|
||||
@@ -96,11 +107,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
def isInvalid(self):
|
||||
return self.__item is None or self.__item.category.name != "Drone"
|
||||
if self._item is None:
|
||||
return True
|
||||
if self._item.category.name != "Drone":
|
||||
return True
|
||||
if self._mutaIsInvalid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self.__item
|
||||
return self._item
|
||||
|
||||
@property
|
||||
def charge(self):
|
||||
@@ -111,9 +128,9 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.hasAmmo:
|
||||
cycleTime = self.getModifiedItemAttr("missileLaunchDuration", 0)
|
||||
else:
|
||||
for attr in ("speed", "duration"):
|
||||
cycleTime = self.getModifiedItemAttr(attr, None)
|
||||
if cycleTime is not None:
|
||||
for attr in ("speed", "duration", "durationHighisGood"):
|
||||
cycleTime = self.getModifiedItemAttr(attr)
|
||||
if cycleTime:
|
||||
break
|
||||
if cycleTime is None:
|
||||
return 0
|
||||
@@ -220,26 +237,38 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
cycleTime = self.cycleTime
|
||||
if cycleTime == 0:
|
||||
if not cycleTime:
|
||||
return None
|
||||
return CycleInfo(self.cycleTime, 0, math.inf, False)
|
||||
|
||||
@property
|
||||
def miningStats(self):
|
||||
if self.__miningyield is None:
|
||||
if self.mines is True and self.amountActive > 0:
|
||||
getter = self.getModifiedItemAttr
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
def miningYPS(self):
|
||||
if self.__miningYield is None:
|
||||
self.__miningYield, self.__miningWaste = self.__calculateMining()
|
||||
return self.__miningYield
|
||||
|
||||
return self.__miningyield
|
||||
@property
|
||||
def miningWPS(self):
|
||||
if self.__miningWaste is None:
|
||||
self.__miningYield, self.__miningWaste = self.__calculateMining()
|
||||
return self.__miningWaste
|
||||
|
||||
def __calculateMining(self):
|
||||
if self.mines is True and self.amountActive > 0:
|
||||
getter = self.getModifiedItemAttr
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
yps = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
yield_ = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
yps = yield_ / (cycleTime / 1000.0)
|
||||
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
|
||||
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
|
||||
wps = yps * max(0, min(1, wasteChance / 100)) * wasteMult
|
||||
return yps, wps
|
||||
else:
|
||||
return 0, 0
|
||||
|
||||
@property
|
||||
def maxRange(self):
|
||||
@@ -285,7 +314,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def clear(self):
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.itemModifiedAttributes.clear()
|
||||
self.chargeModifiedAttributes.clear()
|
||||
|
||||
@@ -337,10 +367,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Drone(self.item)
|
||||
copy = Drone(self.item, self.baseItem, self.mutaplasmid)
|
||||
copy.amount = self.amount
|
||||
copy.amountActive = self.amountActive
|
||||
copy.projectionRange = self.projectionRange
|
||||
self._mutaApplyMutators(mutatorClass=MutatorDrone, targetInstance=copy)
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
@@ -348,16 +379,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
amountActive = self.amountActive
|
||||
projectionRange = self.projectionRange
|
||||
|
||||
Drone.__init__(self, item)
|
||||
Drone.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.amount = amount
|
||||
self.amountActive = amountActive
|
||||
self.projectionRange = projectionRange
|
||||
self._mutaApplyMutators(mutatorClass=MutatorDrone)
|
||||
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
for i in range(1, 3):
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None)
|
||||
if groneGrp is not None:
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
|
||||
if groneGrp:
|
||||
fitDroneGroupLimits.add(int(groneGrp))
|
||||
if len(fitDroneGroupLimits) == 0:
|
||||
return True
|
||||
|
||||
@@ -139,9 +139,11 @@ class Fit:
|
||||
self.__weaponVolleyMap = {}
|
||||
self.__remoteRepMap = {}
|
||||
self.__minerYield = None
|
||||
self.__droneYield = None
|
||||
self.__minerWaste = None
|
||||
self.__droneWaste = None
|
||||
self.__droneDps = None
|
||||
self.__droneVolley = None
|
||||
self.__droneYield = None
|
||||
self.__sustainableTank = None
|
||||
self.__effectiveSustainableTank = None
|
||||
self.__effectiveTank = None
|
||||
@@ -155,7 +157,7 @@ class Fit:
|
||||
self.factorReload = False
|
||||
self.boostsFits = set()
|
||||
self.gangBoosts = None
|
||||
self.ecmProjectedStr = 1
|
||||
self.__ecmProjectedList = []
|
||||
self.commandBonuses = {}
|
||||
|
||||
def clearFactorReloadDependentData(self):
|
||||
@@ -365,21 +367,39 @@ class Fit:
|
||||
@property
|
||||
def minerYield(self):
|
||||
if self.__minerYield is None:
|
||||
self.calculateMiningStats()
|
||||
self.calculatemining()
|
||||
|
||||
return self.__minerYield
|
||||
|
||||
@property
|
||||
def minerWaste(self):
|
||||
if self.__minerWaste is None:
|
||||
self.calculatemining()
|
||||
|
||||
return self.__minerWaste
|
||||
|
||||
@property
|
||||
def droneYield(self):
|
||||
if self.__droneYield is None:
|
||||
self.calculateMiningStats()
|
||||
self.calculatemining()
|
||||
|
||||
return self.__droneYield
|
||||
|
||||
@property
|
||||
def droneWaste(self):
|
||||
if self.__droneWaste is None:
|
||||
self.calculatemining()
|
||||
|
||||
return self.__droneWaste
|
||||
|
||||
@property
|
||||
def totalYield(self):
|
||||
return self.droneYield + self.minerYield
|
||||
|
||||
@property
|
||||
def totalWaste(self):
|
||||
return self.droneWaste + self.minerWaste
|
||||
|
||||
@property
|
||||
def maxTargets(self):
|
||||
maxTargets = min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||
@@ -411,7 +431,11 @@ class Fit:
|
||||
|
||||
@property
|
||||
def jamChance(self):
|
||||
return (1 - self.ecmProjectedStr) * 100
|
||||
sensors = self.scanStrength
|
||||
retainLockChance = 1
|
||||
for jamStr in self.__ecmProjectedList:
|
||||
retainLockChance *= 1 - min(1, jamStr / sensors)
|
||||
return (1 - retainLockChance) * 100
|
||||
|
||||
@property
|
||||
def maxSpeed(self):
|
||||
@@ -487,11 +511,13 @@ class Fit:
|
||||
self.__weaponVolleyMap = {}
|
||||
self.__remoteRepMap = {}
|
||||
self.__minerYield = None
|
||||
self.__droneYield = None
|
||||
self.__minerWaste = None
|
||||
self.__droneWaste = None
|
||||
self.__effectiveSustainableTank = None
|
||||
self.__sustainableTank = None
|
||||
self.__droneDps = None
|
||||
self.__droneVolley = None
|
||||
self.__droneYield = None
|
||||
self.__ehp = None
|
||||
self.__calculated = False
|
||||
self.__capStable = None
|
||||
@@ -499,7 +525,7 @@ class Fit:
|
||||
self.__capUsed = None
|
||||
self.__capRecharge = None
|
||||
self.__savedCapSimData.clear()
|
||||
self.ecmProjectedStr = 1
|
||||
self.__ecmProjectedList = []
|
||||
# self.commandBonuses = {}
|
||||
|
||||
del self.__calculatedTargets[:]
|
||||
@@ -562,6 +588,9 @@ class Fit:
|
||||
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value):
|
||||
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
|
||||
|
||||
def addProjectedEcm(self, strength):
|
||||
self.__ecmProjectedList.append(strength)
|
||||
|
||||
def __runCommandBoosts(self, runTime="normal"):
|
||||
pyfalog.debug("Applying gang boosts for {0}", repr(self))
|
||||
for warfareBuffID in list(self.commandBonuses.keys()):
|
||||
@@ -1620,18 +1649,23 @@ class Fit:
|
||||
else:
|
||||
return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0
|
||||
|
||||
def calculateMiningStats(self):
|
||||
def calculatemining(self):
|
||||
minerYield = 0
|
||||
minerWaste = 0
|
||||
droneYield = 0
|
||||
droneWaste = 0
|
||||
|
||||
for mod in self.modules:
|
||||
minerYield += mod.miningStats
|
||||
|
||||
minerYield += mod.miningYPS
|
||||
minerWaste += mod.miningWPS
|
||||
for drone in self.drones:
|
||||
droneYield += drone.miningStats
|
||||
droneYield += drone.miningYPS
|
||||
droneWaste += drone.miningWPS
|
||||
|
||||
self.__minerYield = minerYield
|
||||
self.__minerWaste = minerWaste
|
||||
self.__droneYield = droneYield
|
||||
self.__droneWaste = droneWaste
|
||||
|
||||
def calculateWeaponDmgStats(self, spoolOptions):
|
||||
weaponVolley = DmgTypes(0, 0, 0, 0)
|
||||
|
||||
@@ -23,7 +23,6 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
|
||||
class Mode(ItemAttrShortcut, HandledItem):
|
||||
|
||||
|
||||
def __init__(self, item, owner=None):
|
||||
if item.group.name != "Ship Modifiers":
|
||||
raise ValueError(
|
||||
@@ -34,7 +33,6 @@ class Mode(ItemAttrShortcut, HandledItem):
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.item.overrides
|
||||
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self.__item
|
||||
|
||||
@@ -27,7 +27,8 @@ from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
|
||||
from eos.saveddata.mutator import MutatorModule
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.float import floatUnerr
|
||||
@@ -61,7 +62,7 @@ ProjectedSystem = {
|
||||
}
|
||||
|
||||
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
|
||||
"""An instance of this class represents a module together with its charge and modified attributes"""
|
||||
MINING_ATTRIBUTES = ("miningAmount",)
|
||||
SYSTEM_GROUPS = (
|
||||
@@ -72,21 +73,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"""Initialize a module from the program"""
|
||||
|
||||
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
|
||||
self._item = item
|
||||
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
|
||||
|
||||
if item is not None and self.isInvalid:
|
||||
raise ValueError("Passed item is not a Module")
|
||||
@@ -101,27 +90,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a module from the database and validate"""
|
||||
self.__item = None
|
||||
self.__baseItem = None
|
||||
self._item = None
|
||||
self.__charge = None
|
||||
self.__mutaplasmid = None
|
||||
|
||||
# we need this early if module is invalid and returns early
|
||||
self.__slot = self.dummySlot
|
||||
|
||||
if self.itemID:
|
||||
self.__item = eos.db.getItem(self.itemID)
|
||||
if self.__item is None:
|
||||
self._item = eos.db.getItem(self.itemID)
|
||||
if self._item is None:
|
||||
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
|
||||
try:
|
||||
self._mutaReconstruct()
|
||||
except MutaError:
|
||||
return
|
||||
|
||||
if self.isInvalid:
|
||||
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
|
||||
@@ -140,7 +124,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.__reloadTime = None
|
||||
self.__reloadForce = None
|
||||
self.__chargeCycles = None
|
||||
@@ -149,21 +134,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
|
||||
self.__slot = self.dummySlot # defaults to None
|
||||
|
||||
if self.__item:
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
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.
|
||||
if self._item:
|
||||
self.__itemModifiedAttributes.original = self._item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self._item.overrides
|
||||
self.__hardpoint = self.__calculateHardpoint(self._item)
|
||||
self.__slot = self.calculateSlot(self._item)
|
||||
|
||||
self._mutaLoadMutators(mutatorClass=MutatorModule)
|
||||
self.__itemModifiedAttributes.mutators = self.mutators
|
||||
|
||||
if self.__charge:
|
||||
@@ -198,28 +175,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# todo: validate baseItem as well if it's set.
|
||||
if self.isEmpty:
|
||||
return False
|
||||
if self.__item is None:
|
||||
if self._item is None:
|
||||
return True
|
||||
if (
|
||||
self.__item.category.name not in ("Module", "Subsystem", "Structure Module")
|
||||
and self.__item.group.name not in self.SYSTEM_GROUPS
|
||||
self._item.category.name not in ("Module", "Subsystem", "Structure Module")
|
||||
and self._item.group.name not in self.SYSTEM_GROUPS
|
||||
):
|
||||
return True
|
||||
if (
|
||||
self.__item.category.name == "Structure Module"
|
||||
and self.__item.group.name == "Quantum Cores"
|
||||
self._item.category.name == "Structure Module"
|
||||
and self._item.group.name == "Quantum Cores"
|
||||
):
|
||||
return True
|
||||
if self.item.isAbyssal and not self.isMutated:
|
||||
return True
|
||||
if self.isMutated and not self.__mutaplasmid:
|
||||
if self._mutaIsInvalid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def isMutated(self):
|
||||
return self.baseItemID and self.mutaplasmidID
|
||||
|
||||
@property
|
||||
def numCharges(self):
|
||||
return self.getNumCharges(self.charge)
|
||||
@@ -336,10 +307,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"shipScanRange", "surveyScanRange")
|
||||
maxRange = None
|
||||
for attr in attrs:
|
||||
maxRange = self.getModifiedItemAttr(attr, None)
|
||||
if maxRange is not None:
|
||||
maxRange = self.getModifiedItemAttr(attr)
|
||||
if maxRange:
|
||||
break
|
||||
if maxRange is not None:
|
||||
if maxRange:
|
||||
if 'burst projector' in self.item.name.lower():
|
||||
maxRange -= self.owner.ship.getModifiedItemAttr("radius")
|
||||
return maxRange
|
||||
@@ -401,8 +372,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def falloff(self):
|
||||
attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff")
|
||||
for attr in attrs:
|
||||
falloff = self.getModifiedItemAttr(attr, None)
|
||||
if falloff is not None:
|
||||
falloff = self.getModifiedItemAttr(attr)
|
||||
if falloff:
|
||||
return falloff
|
||||
|
||||
@property
|
||||
@@ -419,15 +390,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
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
|
||||
return self._item if self._item != 0 else None
|
||||
|
||||
@property
|
||||
def charge(self):
|
||||
@@ -448,27 +411,37 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__itemModifiedAttributes.clear()
|
||||
|
||||
@property
|
||||
def miningStats(self):
|
||||
if self.__miningyield is None:
|
||||
if self.isEmpty:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
if self.state >= FittingModuleState.ACTIVE:
|
||||
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
|
||||
"miningAmount") or 0
|
||||
if volley:
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
def miningYPS(self):
|
||||
if self.__miningYield is None:
|
||||
self.__miningYield, self.__miningWaste = self.__calculateMining()
|
||||
return self.__miningYield
|
||||
|
||||
return self.__miningyield
|
||||
@property
|
||||
def miningWPS(self):
|
||||
if self.__miningWaste is None:
|
||||
self.__miningYield, self.__miningWaste = self.__calculateMining()
|
||||
return self.__miningWaste
|
||||
|
||||
def __calculateMining(self):
|
||||
if self.isEmpty:
|
||||
return 0, 0
|
||||
if self.state >= FittingModuleState.ACTIVE:
|
||||
yield_ = self.getModifiedItemAttr("miningAmount")
|
||||
if yield_:
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
yps = 0
|
||||
else:
|
||||
cycleTime = cycleParams.averageTime
|
||||
yps = yield_ / (cycleTime / 1000.0)
|
||||
else:
|
||||
yps = 0
|
||||
else:
|
||||
yps = 0
|
||||
wasteChance = self.getModifiedItemAttr("miningWasteProbability")
|
||||
wasteMult = self.getModifiedItemAttr("miningWastedVolumeMultiplier")
|
||||
wps = yps * max(0, min(1, wasteChance / 100)) * wasteMult
|
||||
return yps, wps
|
||||
|
||||
def isDealingDamage(self, ignoreState=False):
|
||||
volleyParams = self.getVolleyParameters(ignoreState=ignoreState)
|
||||
@@ -539,7 +512,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
return volleyParams[min(volleyParams)]
|
||||
|
||||
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
||||
def getDps(self, spoolOptions=None, targetProfile=None, ignoreState=False, getSpreadDPS=False):
|
||||
dmgDuringCycle = DmgTypes(0, 0, 0, 0)
|
||||
cycleParams = self.getCycleParameters()
|
||||
if cycleParams is None:
|
||||
@@ -556,7 +529,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
thermal=dmgDuringCycle.thermal * dpsFactor,
|
||||
kinetic=dmgDuringCycle.kinetic * dpsFactor,
|
||||
explosive=dmgDuringCycle.explosive * dpsFactor)
|
||||
return dps
|
||||
if not getSpreadDPS:
|
||||
return dps
|
||||
return {'em':dmgDuringCycle.em * dpsFactor,
|
||||
'therm': dmgDuringCycle.thermal * dpsFactor,
|
||||
'kin': dmgDuringCycle.kinetic * dpsFactor,
|
||||
'exp': dmgDuringCycle.explosive * dpsFactor}
|
||||
|
||||
def isRemoteRepping(self, ignoreState=False):
|
||||
repParams = self.getRepAmountParameters(ignoreState=ignoreState)
|
||||
@@ -713,8 +691,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
|
||||
# Check max group fitted
|
||||
max = self.getModifiedItemAttr("maxGroupFitted", None)
|
||||
if max is not None:
|
||||
max = self.getModifiedItemAttr("maxGroupFitted")
|
||||
if max:
|
||||
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 and
|
||||
@@ -767,7 +745,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# Check if the local module is over it's max limit; if it's not, we're fine
|
||||
maxGroupOnline = self.getModifiedItemAttr("maxGroupOnline", None)
|
||||
maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None)
|
||||
if maxGroupOnline is None and maxGroupActive is None and projectedOnto is None:
|
||||
if not maxGroupOnline and not maxGroupActive and projectedOnto is None:
|
||||
return True
|
||||
|
||||
# Following is applicable only to local modules, we do not want to limit projected
|
||||
@@ -783,11 +761,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
currOnline += 1
|
||||
if mod.state >= FittingModuleState.ACTIVE:
|
||||
currActive += 1
|
||||
if maxGroupOnline is not None and currOnline > maxGroupOnline:
|
||||
if maxGroupOnline and currOnline > maxGroupOnline:
|
||||
if maxState is None or maxState > FittingModuleState.OFFLINE:
|
||||
maxState = FittingModuleState.OFFLINE
|
||||
break
|
||||
if maxGroupActive is not None and currActive > maxGroupActive:
|
||||
if maxGroupActive and currActive > maxGroupActive:
|
||||
if maxState is None or maxState > FittingModuleState.ONLINE:
|
||||
maxState = FittingModuleState.ONLINE
|
||||
return True if maxState is None else maxState
|
||||
@@ -825,7 +803,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
chargeGroup = charge.groupID
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is None:
|
||||
if not itemChargeGroup:
|
||||
continue
|
||||
if itemChargeGroup == chargeGroup:
|
||||
return True
|
||||
@@ -836,7 +814,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
validCharges = set()
|
||||
for i in range(5):
|
||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
|
||||
if itemChargeGroup is not None:
|
||||
if itemChargeGroup:
|
||||
g = eos.db.getGroup(int(itemChargeGroup), eager="items.attributes")
|
||||
if g is None:
|
||||
continue
|
||||
@@ -898,7 +876,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def clear(self):
|
||||
self.__baseVolley = None
|
||||
self.__baseRRAmount = None
|
||||
self.__miningyield = None
|
||||
self.__miningYield = None
|
||||
self.__miningWaste = None
|
||||
self.__reloadTime = None
|
||||
self.__reloadForce = None
|
||||
self.__chargeCycles = None
|
||||
@@ -1021,6 +1000,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
speed = max(
|
||||
self.getModifiedItemAttr("speed", 0), # Most weapons
|
||||
self.getModifiedItemAttr("duration", 0), # Most average modules
|
||||
self.getModifiedItemAttr("durationHighisGood", 0), # Most average modules
|
||||
self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0),
|
||||
self.getModifiedItemAttr("durationECMJammerBurstProjector", 0),
|
||||
@@ -1092,9 +1072,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copy.spoolType = self.spoolType
|
||||
copy.spoolAmount = self.spoolAmount
|
||||
copy.projectionRange = self.projectionRange
|
||||
|
||||
for x in self.mutators.values():
|
||||
Mutator(copy, x.attribute, x.value)
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
|
||||
|
||||
return copy
|
||||
|
||||
@@ -1112,14 +1090,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.spoolType = spoolType
|
||||
self.spoolAmount = spoolAmount
|
||||
self.projectionRange = projectionRange
|
||||
for x in self.mutators.values():
|
||||
Mutator(self, x.attribute, x.value)
|
||||
self._mutaApplyMutators(mutatorClass=MutatorModule)
|
||||
|
||||
def __repr__(self):
|
||||
if self.item:
|
||||
return "Module(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
return "Module(ID={}, name={}) at {}".format(self.item.ID, self.item.name, hex(id(self)))
|
||||
else:
|
||||
return "EmptyModule() at {}".format(hex(id(self)))
|
||||
|
||||
|
||||
105
eos/saveddata/mutatedMixin.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import eos.db
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class MutaError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MutatedMixin:
|
||||
|
||||
@property
|
||||
def isMutated(self):
|
||||
return bool(self.baseItemID and self.mutaplasmidID)
|
||||
|
||||
@property
|
||||
def baseItem(self):
|
||||
return self.__baseItem
|
||||
|
||||
@property
|
||||
def mutaplasmid(self):
|
||||
return self.__mutaplasmid
|
||||
|
||||
@property
|
||||
def fullName(self):
|
||||
if self.isMutated:
|
||||
mutaShortName = self.mutaplasmid.shortName
|
||||
mutaFullName = self.mutaplasmid.item.customName
|
||||
# Short name can be unavailable for non-english language
|
||||
if mutaShortName != mutaFullName:
|
||||
return f'{self.mutaplasmid.shortName} {self.baseItem.customName}'
|
||||
return self.item.customName
|
||||
|
||||
def _mutaInit(self, baseItem, mutaplasmid):
|
||||
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._item.ID, self.baseItemID)
|
||||
self.__baseItem = baseItem
|
||||
self.__mutaplasmid = mutaplasmid
|
||||
else:
|
||||
self.__baseItem = None
|
||||
self.__mutaplasmid = None
|
||||
|
||||
def _mutaReconstruct(self):
|
||||
self.__baseItem = None
|
||||
self.__mutaplasmid = None
|
||||
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)
|
||||
raise MutaError
|
||||
|
||||
def _mutaLoadMutators(self, mutatorClass):
|
||||
# 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
|
||||
mutatorClass(self, attr, attr.value)
|
||||
# @todo: remove attributes that are no longer part of the mutaplasmid.
|
||||
|
||||
@property
|
||||
def _mutaIsInvalid(self):
|
||||
if self.item.isAbyssal and not self.isMutated:
|
||||
return True
|
||||
if self.isMutated and not self.__mutaplasmid:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _mutaApplyMutators(self, mutatorClass, targetInstance=None):
|
||||
if targetInstance is None:
|
||||
targetInstance = self
|
||||
for x in self.mutators.values():
|
||||
mutatorClass(targetInstance, x.attribute, x.value)
|
||||
@@ -27,10 +27,10 @@ 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.
|
||||
class MutatorBase(EqBase):
|
||||
""" Mutators are the object that represent an attribute override on the eos item level, in conjunction with
|
||||
mutaplasmids. Each mutated item, when created, is instantiated with a list of these objects, dictated by the
|
||||
mutaplasmid that is used on the base item.
|
||||
|
||||
A note on the different attributes on this object:
|
||||
* attribute: points to the definition of the attribute from dgmattribs.
|
||||
@@ -40,13 +40,13 @@ class Mutator(EqBase):
|
||||
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
|
||||
def __init__(self, item, attr, value):
|
||||
# this needs to be above item assignment, as assigning the item 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.item = item
|
||||
self.itemID = item.ID
|
||||
|
||||
self.__attr = attr
|
||||
self.build()
|
||||
@@ -67,20 +67,20 @@ class Mutator(EqBase):
|
||||
|
||||
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
|
||||
# But put it here to remove the eos item 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)
|
||||
self.dynamicAttribute = next(a for a in self.item.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]
|
||||
self.baseAttribute = self.item.baseItem.attributes[self.dynamicAttribute.name]
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
self.module = None
|
||||
self.item = None
|
||||
|
||||
@validates("value")
|
||||
def validator(self, key, val):
|
||||
""" Validates values as properly falling within the range of the modules' Mutaplasmid """
|
||||
""" Validates values as properly falling within the range of the items' Mutaplasmid """
|
||||
if self.baseValue == 0:
|
||||
return 0
|
||||
mod = val / self.baseValue
|
||||
@@ -99,7 +99,7 @@ class Mutator(EqBase):
|
||||
# @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)
|
||||
# 3) if a mutaplasmid does not exist (in eve or on the pyfa item's item)
|
||||
# Can remove invalid ones in a SQLAlchemy collection class... eventually
|
||||
return self.__attr is None
|
||||
|
||||
@@ -139,3 +139,11 @@ class Mutator(EqBase):
|
||||
@property
|
||||
def attribute(self):
|
||||
return self.__attr
|
||||
|
||||
|
||||
class MutatorModule(MutatorBase):
|
||||
pass
|
||||
|
||||
|
||||
class MutatorDrone(MutatorBase):
|
||||
pass
|
||||
|
||||
@@ -193,11 +193,8 @@ class DroneView(Display):
|
||||
|
||||
@staticmethod
|
||||
def droneKey(drone):
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
groupName = sMkt.getMarketGroupByItem(drone.item).marketGroupName
|
||||
|
||||
return (DRONE_ORDER.index(groupName), drone.item.name)
|
||||
groupName = Market.getInstance().getMarketGroupByItem(drone.item).marketGroupName
|
||||
return (DRONE_ORDER.index(groupName), drone.isMutated, drone.fullName)
|
||||
|
||||
def fitChanged(self, event):
|
||||
event.Skip()
|
||||
|
||||
@@ -26,7 +26,12 @@ class NotesView(wx.Panel):
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
HandleCtrlBackspace(self.editNotes)
|
||||
try:
|
||||
HandleCtrlBackspace(self.editNotes)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ from gui.builtinContextMenus import itemAmountChange
|
||||
from gui.builtinContextMenus import itemProjectionRange
|
||||
from gui.builtinContextMenus import droneSplitStack
|
||||
from gui.builtinContextMenus import itemVariationChange
|
||||
from gui.builtinContextMenus import moduleMutations
|
||||
from gui.builtinContextMenus import itemMutations
|
||||
from gui.builtinContextMenus import moduleFill
|
||||
from gui.builtinContextMenus import moduleMutatedExport
|
||||
from gui.builtinContextMenus import skillAffectors
|
||||
|
||||
@@ -5,7 +5,7 @@ from gui import fitCommands as cmd
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from gui.utils.clipboard import fromClipboard
|
||||
from service.fit import Fit
|
||||
from service.port.eft import parseAdditions
|
||||
from service.port.eft import parseAdditions, importGetMutationData, lineIter
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
@@ -41,9 +41,12 @@ class AdditionsImport(ContextMenuUnconditional):
|
||||
|
||||
def activate(self, callingWindow, fullContext, i):
|
||||
text = fromClipboard()
|
||||
items = parseAdditions(text)
|
||||
lines = list(lineIter(text))
|
||||
mutaData = importGetMutationData(lines)
|
||||
text = '\n'.join(lines)
|
||||
items = parseAdditions(text, mutaData=mutaData)
|
||||
filterFunc = self.viewSpecMap[self.srcContext][1]
|
||||
items = [(i.ID, a) for i, a in items if filterFunc(i)]
|
||||
items = [(i.ID, a, m) for i, a, m in items if filterFunc(i)]
|
||||
if not items:
|
||||
return
|
||||
command = self.viewSpecMap[self.srcContext][2]
|
||||
|
||||
@@ -8,7 +8,7 @@ from eos.saveddata.cargo import Cargo as es_Cargo
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter as es_Fighter
|
||||
from eos.saveddata.fit import Fit as es_Fit
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from service.fit import Fit
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -16,12 +16,12 @@ from service.fit import Fit
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class ChangeItemAmount(ContextMenuSingle):
|
||||
class ChangeItemAmount(ContextMenuCombined):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem):
|
||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||
if srcContext not in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter"):
|
||||
return False
|
||||
|
||||
@@ -30,10 +30,12 @@ class ChangeItemAmount(ContextMenuSingle):
|
||||
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||
if isinstance(mainItem, es_Cargo):
|
||||
return _t("Change Selection Quantity")
|
||||
return _t("Change {0} Quantity").format(itmContext)
|
||||
|
||||
def activate(self, callingWindow, fullContext, mainItem, i):
|
||||
def activate(self, callingWindow, fullContext, mainItem, selection, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
srcContext = fullContext[0]
|
||||
if isinstance(mainItem, es_Fit):
|
||||
@@ -56,8 +58,12 @@ class ChangeItemAmount(ContextMenuSingle):
|
||||
cleanInput = int(float(re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())))
|
||||
|
||||
if isinstance(mainItem, es_Cargo):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(
|
||||
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
|
||||
itemIDs = []
|
||||
for cargo in selection:
|
||||
if cargo in fit.cargo:
|
||||
itemIDs.append(cargo.itemID)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeCargosAmountCommand(
|
||||
fitID=fitID, itemIDs=itemIDs, amount=cleanInput))
|
||||
elif isinstance(mainItem, Drone):
|
||||
if srcContext == "projectedDrone":
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import re
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from gui.fitCommands import GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand
|
||||
from gui.fitCommands import (
|
||||
GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand,
|
||||
GuiConvertMutatedLocalDroneCommand, GuiRevertMutatedLocalDroneCommand)
|
||||
from service.fit import Fit
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class ChangeModuleMutation(ContextMenuSingle):
|
||||
class ChangeItemMutation(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -18,10 +21,10 @@ class ChangeModuleMutation(ContextMenuSingle):
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem):
|
||||
|
||||
if srcContext != "fittingModule" or self.mainFrame.getActiveFit() is None:
|
||||
if srcContext not in ("fittingModule", "droneItem") or self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
if mainItem is None or mainItem.isEmpty:
|
||||
if mainItem is None or getattr(mainItem, 'isEmpty', False):
|
||||
return False
|
||||
|
||||
if len(mainItem.item.mutaplasmids) == 0 and not mainItem.isMutated:
|
||||
@@ -42,24 +45,27 @@ class ChangeModuleMutation(ContextMenuSingle):
|
||||
|
||||
menu = rootMenu if msw else sub
|
||||
|
||||
for item in mainItem.item.mutaplasmids:
|
||||
label = item.item.name
|
||||
for mutaplasmid in mainItem.item.mutaplasmids:
|
||||
id = ContextMenuSingle.nextID()
|
||||
self.eventIDs[id] = (item, mainItem)
|
||||
skillItem = wx.MenuItem(menu, id, label)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMenu, skillItem)
|
||||
sub.Append(skillItem)
|
||||
self.eventIDs[id] = (mutaplasmid, mainItem)
|
||||
mItem = wx.MenuItem(menu, id, mutaplasmid.shortName)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMenu, mItem)
|
||||
sub.Append(mItem)
|
||||
|
||||
return sub
|
||||
|
||||
def handleMenu(self, event):
|
||||
mutaplasmid, mod = self.eventIDs[event.Id]
|
||||
mutaplasmid, item = self.eventIDs[event.Id]
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if mod in fit.modules:
|
||||
position = fit.modules.index(mod)
|
||||
if item in fit.modules:
|
||||
position = fit.modules.index(item)
|
||||
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
|
||||
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
|
||||
elif item in fit.drones:
|
||||
position = fit.drones.index(item)
|
||||
self.mainFrame.command.Submit(GuiConvertMutatedLocalDroneCommand(
|
||||
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
|
||||
|
||||
def activate(self, callingWindow, fullContext, mainItem, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
@@ -68,9 +74,13 @@ class ChangeModuleMutation(ContextMenuSingle):
|
||||
position = fit.modules.index(mainItem)
|
||||
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
|
||||
fitID=fitID, position=position))
|
||||
elif mainItem in fit.drones:
|
||||
position = fit.drones.index(mainItem)
|
||||
self.mainFrame.command.Submit(GuiRevertMutatedLocalDroneCommand(
|
||||
fitID=fitID, position=position))
|
||||
|
||||
def getBitmap(self, callingWindow, context, mainItem):
|
||||
return None
|
||||
|
||||
|
||||
ChangeModuleMutation.register()
|
||||
ChangeItemMutation.register()
|
||||
@@ -1,5 +1,6 @@
|
||||
# noinspection PyPackageRequirements
|
||||
from collections import OrderedDict
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
@@ -25,8 +26,20 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
'thermal': _t('Thermal'),
|
||||
'explosive': _t('Explosive'),
|
||||
'kinetic': _t('Kinetic'),
|
||||
'mixed': _t('Mixed')
|
||||
}
|
||||
'mixed': _t('Mixed')}
|
||||
self.oreChargeCatTrans = OrderedDict([
|
||||
('a1', _t('Asteroid Simple')),
|
||||
('a2', _t('Asteroid Coherent')),
|
||||
('a3', _t('Asteroid Variegated')),
|
||||
('a4', _t('Asteroid Complex')),
|
||||
('a5', _t('Asteroid Abyssal')),
|
||||
('a6', _t('Asteroid Mercoxit')),
|
||||
('r4', _t('Moon Ubiquitous')),
|
||||
('r8', _t('Moon Common')),
|
||||
('r16', _t('Moon Uncommon')),
|
||||
('r32', _t('Moon Rare')),
|
||||
('r64', _t('Moon Exceptional')),
|
||||
('misc', _t('Misc'))])
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||
if srcContext not in ('fittingModule', 'projectedModule'):
|
||||
@@ -115,6 +128,24 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
self._addSeparator(subMenu, _t('More Damage'))
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
elif modType == 'miner':
|
||||
menuItems = []
|
||||
for catHandle, catLabel in self.oreChargeCatTrans.items():
|
||||
charges = chargeDict.get(catHandle)
|
||||
if not charges:
|
||||
continue
|
||||
if len(charges) == 1:
|
||||
menuItems.append(self._addCharge(rootMenu if msw else menu, charges[0]))
|
||||
else:
|
||||
menuItem = wx.MenuItem(menu, wx.ID_ANY, catLabel)
|
||||
menuItems.append(menuItem)
|
||||
subMenu = wx.Menu()
|
||||
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
for charge in charges:
|
||||
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
elif modType == 'general':
|
||||
for charge in chargeDict['general']:
|
||||
menu.Append(self._addCharge(rootMenu if msw else menu, charge))
|
||||
|
||||
@@ -59,7 +59,8 @@ AttrGroupDict = {
|
||||
"agility",
|
||||
"droneCapacity",
|
||||
"droneBandwidth",
|
||||
"specialOreHoldCapacity",
|
||||
"generalMiningHoldCapacity",
|
||||
"specialIceHoldCapacity",
|
||||
"specialGasHoldCapacity",
|
||||
"specialMineralHoldCapacity",
|
||||
"specialSalvageHoldCapacity",
|
||||
|
||||
@@ -194,6 +194,7 @@ class ItemParams(wx.Panel):
|
||||
|
||||
attrIcon, attrName, currentVal, baseVal = data
|
||||
attr_item = self.paramList.AppendItem(parent, attrName)
|
||||
self.paramList.SetItemTextColour(attr_item, wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
||||
|
||||
self.paramList.SetItemText(attr_item, currentVal, 1)
|
||||
if self.stuff is not None:
|
||||
@@ -222,6 +223,7 @@ class ItemParams(wx.Panel):
|
||||
heading = data.get("label")
|
||||
|
||||
header_item = self.paramList.AppendItem(root, heading)
|
||||
self.paramList.SetItemTextColour(header_item, wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
||||
for attr in data.get("attributes", []):
|
||||
# Attribute is a "grouped" attr (eg: damage, sensor strengths, etc). Automatically group these into a child item
|
||||
if attr in GroupedAttributes:
|
||||
@@ -232,6 +234,7 @@ class ItemParams(wx.Panel):
|
||||
|
||||
# create a child item with the groups label
|
||||
item = self.paramList.AppendItem(header_item, grouping[1])
|
||||
self.paramList.SetItemTextColour(item, wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
||||
for attr2 in grouping[0]:
|
||||
# add each attribute in the group
|
||||
self.AddAttribute(item, attr2)
|
||||
@@ -256,6 +259,7 @@ class ItemParams(wx.Panel):
|
||||
|
||||
# get all attributes in group
|
||||
item = self.paramList.AppendItem(root, grouping[1])
|
||||
self.paramList.SetItemTextColour(item, wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
||||
for attr2 in grouping[0]:
|
||||
self.AddAttribute(item, attr2)
|
||||
|
||||
@@ -351,4 +355,5 @@ class ItemParams(wx.Panel):
|
||||
fvalue = roundDec(value, digits)
|
||||
else:
|
||||
fvalue = value
|
||||
return "%s %s" % (fvalue, unit)
|
||||
unitSuffix = f' {unit}' if unit is not None else ''
|
||||
return f'{fvalue}{unitSuffix}'
|
||||
|
||||
@@ -25,9 +25,9 @@ class ItemDescription(wx.Panel):
|
||||
desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P<inside>.*?)<( *)/( *)font( *)>", "\g<inside>", desc)
|
||||
# Strip URLs
|
||||
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", "\g<inside>", desc)
|
||||
desc = "<body style='background-color: {}; color: {}'>{}</body>".format(
|
||||
bgcolor.GetAsString(wx.C2S_CSS_SYNTAX),
|
||||
fgcolor.GetAsString(wx.C2S_CSS_SYNTAX),
|
||||
desc = "<body bgcolor='{}' text='{}'>{}</body>".format(
|
||||
bgcolor.GetAsString(wx.C2S_HTML_SYNTAX),
|
||||
fgcolor.GetAsString(wx.C2S_HTML_SYNTAX),
|
||||
desc
|
||||
)
|
||||
|
||||
|
||||
@@ -18,23 +18,22 @@ _t = wx.GetTranslation
|
||||
|
||||
class ItemMutatorPanel(wx.Panel):
|
||||
|
||||
def __init__(self, parent, mod):
|
||||
def __init__(self, parent, stuff):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.stuff = mod
|
||||
self.stuff = stuff
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
headerSizer.AddStretchSpacer()
|
||||
itemIcon = BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons")
|
||||
itemIcon = BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons")
|
||||
if itemIcon is not None:
|
||||
headerSizer.Add(itemIcon, 0, 0, 0)
|
||||
mutaIcon = BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons")
|
||||
mutaIcon = BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons")
|
||||
if mutaIcon is not None:
|
||||
headerSizer.Add(mutaIcon, 0, wx.LEFT, 0)
|
||||
sourceItemShort = "{} {}".format(mod.mutaplasmid.item.name.split(" ")[0], mod.baseItem.name)
|
||||
sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
|
||||
sourceItemText = wx.StaticText(self, wx.ID_ANY, stuff.fullName)
|
||||
font = parent.GetFont()
|
||||
font.SetWeight(wx.BOLD)
|
||||
sourceItemText.SetFont(font)
|
||||
@@ -43,7 +42,7 @@ class ItemMutatorPanel(wx.Panel):
|
||||
mainSizer.Add(headerSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
|
||||
|
||||
self.mutaList = ItemMutatorList(self, mod)
|
||||
self.mutaList = ItemMutatorList(self, stuff)
|
||||
mainSizer.Add(self.mutaList, 1, wx.EXPAND | wx.ALL, 0)
|
||||
|
||||
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
|
||||
@@ -68,13 +67,13 @@ class ItemMutatorPanel(wx.Panel):
|
||||
|
||||
class ItemMutatorList(wx.ScrolledWindow):
|
||||
|
||||
def __init__(self, parent, mod):
|
||||
def __init__(self, parent, stuff):
|
||||
wx.ScrolledWindow.__init__(self, parent)
|
||||
self.SetScrollRate(0, 15)
|
||||
self.carryingFitID = gui.mainFrame.MainFrame.getInstance().getActiveFit()
|
||||
self.initialMutations = {}
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
self.mod = mod
|
||||
self.stuff = stuff
|
||||
self.timer = None
|
||||
self.isModified = False
|
||||
|
||||
@@ -91,9 +90,8 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
('Damage Control', 'duration'): True,
|
||||
('Siege Module', 'siegeLocalLogisticsDurationBonus'): False
|
||||
}
|
||||
|
||||
first = True
|
||||
for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
|
||||
for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
|
||||
if m.baseValue == 0:
|
||||
continue
|
||||
if not first:
|
||||
@@ -102,10 +100,10 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
|
||||
self.initialMutations[m.attrID] = m.value
|
||||
|
||||
highIsGood = higOverrides.get((mod.item.group.name, m.attribute.name), m.highIsGood)
|
||||
highIsGood = higOverrides.get((stuff.item.group.name, m.attribute.name), m.highIsGood)
|
||||
# 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))
|
||||
range1 = (m.minValue, self._simplifyValue(m, m.minValue))
|
||||
range2 = (m.maxValue, self._simplifyValue(m, 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
|
||||
@@ -148,11 +146,11 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
|
||||
headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
|
||||
worseVal = ItemParams.FormatValue(*self._preformatValue(m, worseRange[0]), rounding='dec')
|
||||
worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
|
||||
worseText.SetForegroundColour(badColor)
|
||||
|
||||
betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
|
||||
betterVal = ItemParams.FormatValue(*self._preformatValue(m, betterRange[0]), rounding='dec')
|
||||
betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
|
||||
betterText.SetForegroundColour(goodColor)
|
||||
|
||||
@@ -163,23 +161,38 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
sizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
slider = AttributeSlider(parent=self,
|
||||
baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
|
||||
baseValue=self._simplifyValue(m, sliderBaseValue),
|
||||
minValue=displayMinRange[1],
|
||||
maxValue=displayMaxRange[1],
|
||||
inverse=displayMaxRange is worseRange)
|
||||
slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
|
||||
slider.SetValue(self._simplifyValue(m, m.value), False)
|
||||
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
|
||||
self.event_mapping[slider] = m
|
||||
sizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def _simplifyValue(self, mutator, value):
|
||||
if mutator.attribute.unit is None:
|
||||
return value
|
||||
return mutator.attribute.unit.SimplifyValue(value)
|
||||
|
||||
def _complicateValue(self, mutator, value):
|
||||
if mutator.attribute.unit is None:
|
||||
return value
|
||||
return mutator.attribute.unit.ComplicateValue(value)
|
||||
|
||||
def _preformatValue(self, mutator, value):
|
||||
if mutator.attribute.unit is None:
|
||||
return value, None
|
||||
return mutator.attribute.unit.PreformatValue(value)
|
||||
|
||||
def changeMutatedValue(self, evt):
|
||||
if evt.AffectsModifiedFlag:
|
||||
self.isModified = True
|
||||
m = self.event_mapping[evt.Object]
|
||||
value = evt.Value
|
||||
value = m.attribute.unit.ComplicateValue(value)
|
||||
value = self._complicateValue(m, value)
|
||||
sFit = Fit.getInstance()
|
||||
|
||||
sFit.changeMutatedValuePrelim(m, value)
|
||||
@@ -198,7 +211,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
sFit = Fit.getInstance()
|
||||
for slider, m in self.event_mapping.items():
|
||||
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
value = self._simplifyValue(m, value)
|
||||
slider.SetValue(value, affect_modified_flag=False)
|
||||
evt.Skip()
|
||||
|
||||
@@ -208,7 +221,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
for slider, m in self.event_mapping.items():
|
||||
value = random.uniform(m.minValue, m.maxValue)
|
||||
value = sFit.changeMutatedValuePrelim(m, value)
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
value = self._simplifyValue(m, value)
|
||||
slider.SetValue(value, affect_modified_flag=False)
|
||||
evt.Skip()
|
||||
|
||||
@@ -218,7 +231,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
for slider, m in self.event_mapping.items():
|
||||
if m.attrID in self.initialMutations:
|
||||
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
|
||||
value = m.attribute.unit.SimplifyValue(value)
|
||||
value = self._simplifyValue(m, value)
|
||||
slider.SetValue(value, affect_modified_flag=False)
|
||||
evt.Skip()
|
||||
|
||||
@@ -226,25 +239,34 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
# Submit mutation changes
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.carryingFitID)
|
||||
if self.mod in fit.modules:
|
||||
isCurrentMod = self.stuff in fit.modules
|
||||
isCurrentDrone = self.stuff in fit.drones
|
||||
if isCurrentMod or isCurrentDrone:
|
||||
if self.isModified:
|
||||
currentMutation = {}
|
||||
for slider, m in self.event_mapping.items():
|
||||
# Sliders may have more up-to-date info than mutator in case we changed
|
||||
# value in slider and without confirming it, decided to close window
|
||||
value = slider.GetValue()
|
||||
value = m.attribute.unit.ComplicateValue(value)
|
||||
value = self._complicateValue(m, value)
|
||||
if value != m.value:
|
||||
value = sFit.changeMutatedValuePrelim(m, value)
|
||||
currentMutation[m.attrID] = value
|
||||
else:
|
||||
currentMutation = self.initialMutations
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
|
||||
fitID=self.carryingFitID,
|
||||
position=fit.modules.index(self.mod),
|
||||
mutation=currentMutation,
|
||||
oldMutation=self.initialMutations))
|
||||
if isCurrentMod:
|
||||
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
|
||||
fitID=self.carryingFitID,
|
||||
position=fit.modules.index(self.stuff),
|
||||
mutation=currentMutation,
|
||||
oldMutation=self.initialMutations))
|
||||
elif isCurrentDrone:
|
||||
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalDroneMutationCommand(
|
||||
fitID=self.carryingFitID,
|
||||
position=fit.drones.index(self.stuff),
|
||||
mutation=currentMutation,
|
||||
oldMutation=self.initialMutations))
|
||||
for slider in self.event_mapping:
|
||||
slider.OnWindowClose()
|
||||
|
||||
|
||||
@@ -13,7 +13,12 @@ class ItemTraits(wx.Panel):
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.traits = wx.html.HtmlWindow(self)
|
||||
self.traits.SetPage(item.traits.display)
|
||||
|
||||
bgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
|
||||
fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
|
||||
self.traits.SetPage("<body bgcolor='{}' text='{}'>{}</body>".format(
|
||||
bgcolor.GetAsString(wx.C2S_HTML_SYNTAX),
|
||||
fgcolor.GetAsString(wx.C2S_HTML_SYNTAX), item.traits.display))
|
||||
|
||||
self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
||||
self.traits.Bind(wx.EVT_KEY_UP, self.onKeyUp)
|
||||
|
||||
@@ -37,118 +37,19 @@ class PFEsiPref(PreferenceView):
|
||||
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize,
|
||||
[_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbMode.SetItemToolTip(0, _t("This options starts a local webserver that the web application will call back to"
|
||||
self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to"
|
||||
" with information about the character login."))
|
||||
self.rbMode.SetItemToolTip(1, _t("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, _t("SSO Mode"), wx.DefaultPosition, wx.DefaultSize,
|
||||
[_t('pyfa.io'), _t('Custom application')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbSsoMode.SetItemToolTip(0, _t("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, _t("This option goes through EVE SSO directly, but requires more configuration. Use "
|
||||
"this if pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io."))
|
||||
self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for"
|
||||
" character login. Use this if having issues with the local server."))
|
||||
|
||||
self.rbMode.SetSelection(self.settings.get('loginMode'))
|
||||
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)
|
||||
|
||||
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
|
||||
self.rbSsoMode.Bind(wx.EVT_RADIOBOX, self.OnSSOChange)
|
||||
|
||||
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
detailsTitle = wx.StaticText(panel, wx.ID_ANY, _t("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, _t("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, _t("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)
|
||||
#
|
||||
# timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
# self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout'))
|
||||
# timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5)
|
||||
# self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange)
|
||||
#
|
||||
# mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
# detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", 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, "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, "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.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
# self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply)
|
||||
#
|
||||
# mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
|
||||
# mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5)
|
||||
|
||||
# self.ToggleProxySettings(self.settings.get('loginMode'))
|
||||
|
||||
self.ToggleSSOMode(self.settings.get('ssoMode'))
|
||||
panel.SetSizer(mainSizer)
|
||||
panel.Layout()
|
||||
|
||||
@@ -158,32 +59,6 @@ class PFEsiPref(PreferenceView):
|
||||
def OnModeChange(self, event):
|
||||
self.settings.set('loginMode', event.GetInt())
|
||||
|
||||
def OnSSOChange(self, event):
|
||||
self.settings.set('ssoMode', event.GetInt())
|
||||
self.ToggleSSOMode(event.GetInt())
|
||||
|
||||
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()
|
||||
#
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("eve", "gui")
|
||||
|
||||
|
||||
@@ -149,6 +149,14 @@ class PFGeneralPref(PreferenceView):
|
||||
_t('When disabled, reloads charges just in selected modules. Action can be reversed by holding Ctrl or Alt key while changing charge.')))
|
||||
mainSizer.Add(self.cbReloadAll, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.cbExpMutants = wx.CheckBox(panel, wx.ID_ANY, _t("Include more information in names of mutated items"),
|
||||
wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
if "wxGTK" not in wx.PlatformInfo:
|
||||
self.cbExpMutants.SetCursor(helpCursor)
|
||||
self.cbExpMutants.SetToolTip(wx.ToolTip(
|
||||
_t('Use short mutaplasmid name and base item name instead of actual item name. Works if EVE data language is set to English.')))
|
||||
mainSizer.Add(self.cbExpMutants, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.rbAddLabels = wx.RadioBox(panel, -1, _t("Extra info in Additions panel tab names"), wx.DefaultPosition, wx.DefaultSize,
|
||||
[_t("None"), _t("Quantity of active items"), _t("Quantity of all items")], 1, wx.RA_SPECIFY_COLS)
|
||||
mainSizer.Add(self.rbAddLabels, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.BOTTOM, 10)
|
||||
@@ -169,6 +177,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.cbOpenFitInNew.SetValue(self.sFit.serviceFittingOptions["openFitInNew"])
|
||||
self.cbShowShipBrowserTooltip.SetValue(self.sFit.serviceFittingOptions["showShipBrowserTooltip"])
|
||||
self.cbReloadAll.SetValue(self.sFit.serviceFittingOptions["ammoChangeAll"])
|
||||
self.cbExpMutants.SetValue(self.sFit.serviceFittingOptions["expandedMutantNames"])
|
||||
self.rbAddLabels.SetSelection(self.sFit.serviceFittingOptions["additionsLabels"])
|
||||
|
||||
self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange)
|
||||
@@ -184,6 +193,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.cbOpenFitInNew.Bind(wx.EVT_CHECKBOX, self.onCBOpenFitInNew)
|
||||
self.cbShowShipBrowserTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowShipBrowserTooltip)
|
||||
self.cbReloadAll.Bind(wx.EVT_CHECKBOX, self.onCBReloadAll)
|
||||
self.cbExpMutants.Bind(wx.EVT_CHECKBOX, self.onCBExpMutants)
|
||||
|
||||
self.cbRackLabels.Enable(self.sFit.serviceFittingOptions["rackSlots"] or False)
|
||||
|
||||
@@ -266,6 +276,11 @@ class PFGeneralPref(PreferenceView):
|
||||
def onCBReloadAll(self, event):
|
||||
self.sFit.serviceFittingOptions["ammoChangeAll"] = self.cbReloadAll.GetValue()
|
||||
|
||||
def onCBExpMutants(self, event):
|
||||
self.sFit.serviceFittingOptions["expandedMutantNames"] = self.cbExpMutants.GetValue()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitIDs=(fitID,)))
|
||||
|
||||
def OnAddLabelsChange(self, event):
|
||||
self.sFit.serviceFittingOptions["additionsLabels"] = event.GetInt()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
|
||||
@@ -130,21 +130,33 @@ class MiningYieldViewFull(StatsView):
|
||||
def refreshPanel(self, fit):
|
||||
# If we did anything intresting, we'd update our labels to reflect the new fit's stats here
|
||||
|
||||
stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, "%s m\u00B3/s", None),
|
||||
("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, "%s m\u00B3/s", None),
|
||||
("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, "%s m\u00B3/s", None))
|
||||
stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, lambda: fit.minerWaste, 3, 0, 0, "{}{} m\u00B3/s", None),
|
||||
("labelFullminingyieldDrone", lambda: fit.droneYield, lambda: fit.droneWaste, 3, 0, 0, "{}{} m\u00B3/s", None),
|
||||
("labelFullminingyieldTotal", lambda: fit.totalYield, lambda: fit.totalWaste, 3, 0, 0, "{}{} m\u00B3/s", None))
|
||||
|
||||
counter = 0
|
||||
for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
|
||||
label = getattr(self, labelName)
|
||||
def processValue(value):
|
||||
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 = "Mining Yield per second ({0} per hour)".format(formatAmount(value * 3600, 3, 0, 3))
|
||||
label.SetToolTip(wx.ToolTip(tipStr))
|
||||
self._cachedValues[counter] = value
|
||||
return value
|
||||
|
||||
counter = 0
|
||||
for labelName, yieldValue, wasteValue, prec, lowest, highest, valueFormat, altFormat in stats:
|
||||
label = getattr(self, labelName)
|
||||
yieldValue = processValue(yieldValue)
|
||||
wasteValue = processValue(wasteValue)
|
||||
if self._cachedValues[counter] != (yieldValue, wasteValue):
|
||||
yps = formatAmount(yieldValue, prec, lowest, highest)
|
||||
yph = formatAmount(yieldValue * 3600, prec, lowest, highest)
|
||||
wps = formatAmount(wasteValue, prec, lowest, highest)
|
||||
wph = formatAmount(wasteValue * 3600, prec, lowest, highest)
|
||||
wasteSuffix = '\u02b7' if wasteValue > 0 else ''
|
||||
label.SetLabel(valueFormat.format(yps, wasteSuffix))
|
||||
tipLines = []
|
||||
tipLines.append("{} m\u00B3 mining yield per second ({} m\u00B3 per hour)".format(yps, yph))
|
||||
if wasteValue > 0:
|
||||
tipLines.append("{} m\u00B3 mining waste per second ({} m\u00B3 per hour)".format(wps, wph))
|
||||
label.SetToolTip(wx.ToolTip('\n'.join(tipLines)))
|
||||
self._cachedValues[counter] = (yieldValue, wasteValue)
|
||||
counter += 1
|
||||
self.panel.Layout()
|
||||
self.headerPanel.Layout()
|
||||
|
||||
@@ -119,10 +119,11 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
("specialMediumShipHoldCapacity", _t("Medium ship hold")),
|
||||
("specialLargeShipHoldCapacity", _t("Large ship hold")),
|
||||
("specialIndustrialShipHoldCapacity", _t("Industrial ship hold")),
|
||||
("specialOreHoldCapacity", _t("Ore hold")),
|
||||
("generalMiningHoldCapacity", _t("Mining hold")),
|
||||
("specialIceHoldCapacity", _t("Ice hold")),
|
||||
("specialGasHoldCapacity", _t("Gas hold")),
|
||||
("specialMineralHoldCapacity", _t("Mineral hold")),
|
||||
("specialMaterialBayCapacity", _t("Material bay")),
|
||||
("specialGasHoldCapacity", _t("Gas hold")),
|
||||
("specialSalvageHoldCapacity", _t("Salvage hold")),
|
||||
("specialCommandCenterHoldCapacity", _t("Command center hold")),
|
||||
("specialPlanetaryCommoditiesHoldCapacity", _t("Planetary goods hold")),
|
||||
@@ -139,10 +140,11 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
"specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"),
|
||||
"specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"),
|
||||
"specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialIndustrialShipHoldCapacity"),
|
||||
"specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"),
|
||||
"generalMiningHoldCapacity": lambda: fit.ship.getModifiedItemAttr("generalMiningHoldCapacity"),
|
||||
"specialIceHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialIceHoldCapacity"),
|
||||
"specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"),
|
||||
"specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"),
|
||||
"specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"),
|
||||
"specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"),
|
||||
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
|
||||
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"),
|
||||
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"),
|
||||
|
||||
@@ -80,7 +80,7 @@ class AttributeDisplay(ViewColumn):
|
||||
else:
|
||||
attr = mod.getAttribute(self.info.name)
|
||||
|
||||
if attr is None:
|
||||
if not attr:
|
||||
return ""
|
||||
|
||||
if self.info.name == "volume":
|
||||
|
||||
@@ -70,7 +70,10 @@ class BaseName(ViewColumn):
|
||||
stuff = stuff.item
|
||||
|
||||
if isinstance(stuff, Drone):
|
||||
return "%dx %s" % (stuff.amount, stuff.item.name)
|
||||
if FitSvc.getInstance().serviceFittingOptions["expandedMutantNames"]:
|
||||
return "%dx %s" % (stuff.amount, stuff.fullName)
|
||||
else:
|
||||
return "%dx %s" % (stuff.amount, stuff.item.name)
|
||||
elif isinstance(stuff, Fighter):
|
||||
return "%d/%d %s" % \
|
||||
(stuff.amount, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name)
|
||||
@@ -117,7 +120,10 @@ class BaseName(ViewColumn):
|
||||
if stuff.isEmpty:
|
||||
return "%s Slot" % FittingSlot(stuff.slot).name.capitalize()
|
||||
else:
|
||||
return stuff.item.customName
|
||||
if FitSvc.getInstance().serviceFittingOptions["expandedMutantNames"]:
|
||||
return stuff.fullName
|
||||
else:
|
||||
return stuff.item.customName
|
||||
elif isinstance(stuff, Implant):
|
||||
return stuff.item.name
|
||||
elif isinstance(stuff, TargetProfile):
|
||||
|
||||
@@ -537,14 +537,24 @@ class Miscellanea(ViewColumn):
|
||||
text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3))
|
||||
tooltip = "Optimal signature radius"
|
||||
return text, tooltip
|
||||
elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester", "Mining Drone"):
|
||||
miningAmount = stuff.getModifiedItemAttr("specialtyMiningAmount") or stuff.getModifiedItemAttr("miningAmount")
|
||||
cycleTime = getattr(stuff, 'cycleTime', stuff.getModifiedItemAttr("duration"))
|
||||
if not miningAmount or not cycleTime:
|
||||
elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Scoops", "Mining Drone", "Gas Cloud Harvesters"):
|
||||
yps = stuff.miningYPS
|
||||
if not yps:
|
||||
return "", None
|
||||
minePerSec = (float(miningAmount) * 1000 / cycleTime)
|
||||
text = "{0} m3/s".format(formatAmount(minePerSec, 3, 0, 3))
|
||||
tooltip = "Mining Yield per second ({0} per hour)".format(formatAmount(minePerSec * 3600, 3, 0, 3))
|
||||
yph = yps * 3600
|
||||
wps = stuff.miningWPS
|
||||
wph = wps * 3600
|
||||
textParts = []
|
||||
textParts.append(formatAmount(yps, 3, 0, 3))
|
||||
tipLines = []
|
||||
tipLines.append("{} m\u00B3 mining yield per second ({} m\u00B3 per hour)".format(
|
||||
formatAmount(yps, 3, 0, 3), formatAmount(yph, 3, 0, 3)))
|
||||
if wps > 0:
|
||||
textParts.append(formatAmount(wps, 3, 0, 3))
|
||||
tipLines.append("{} m\u00B3 mining waste per second ({} m\u00B3 per hour)".format(
|
||||
formatAmount(wps, 3, 0, 3), formatAmount(wph, 3, 0, 3)))
|
||||
text = '{} m\u00B3/s'.format('+'.join(textParts))
|
||||
tooltip = '\n'.join(tipLines)
|
||||
return text, tooltip
|
||||
elif itemGroup == "Logistic Drone":
|
||||
rpsData = stuff.getRemoteReps(ignoreState=True)
|
||||
@@ -679,7 +689,7 @@ class Miscellanea(ViewColumn):
|
||||
formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3),
|
||||
formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3),
|
||||
)
|
||||
tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format(
|
||||
tooltip = "Resistances shifted to damage profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format(
|
||||
formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3),
|
||||
formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3),
|
||||
formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3),
|
||||
@@ -693,6 +703,80 @@ class Miscellanea(ViewColumn):
|
||||
text = "{}s".format(formatAmount(duration / 1000, 3, 0, 0))
|
||||
tooltip = "Scan duration"
|
||||
return text, tooltip
|
||||
elif itemGroup == "Command Burst":
|
||||
textSections = []
|
||||
tooltipSections = []
|
||||
buffMap = {}
|
||||
for seq in (1, 2, 3, 4):
|
||||
buffId = stuff.getModifiedChargeAttr(f'warfareBuff{seq}ID')
|
||||
if not buffId:
|
||||
continue
|
||||
buffValue = stuff.getModifiedItemAttr(f'warfareBuff{seq}Value')
|
||||
buffMap[buffId] = buffValue
|
||||
if buffId == 10: # Shield Burst: Shield Harmonizing: Shield Resistance
|
||||
# minus buff value because ingame shows positive value
|
||||
textSections.append(f"{formatAmount(-buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("shield resistance")
|
||||
elif buffId == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("shield RR duration & capacictor use")
|
||||
elif buffId == 12: # Shield Burst: Shield Extension: Shield HP
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("shield HP")
|
||||
elif buffId == 13: # Armor Burst: Armor Energizing: Armor Resistance
|
||||
# minus buff value because ingame shows positive value
|
||||
textSections.append(f"{formatAmount(-buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("armor resistance")
|
||||
elif buffId == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("armor RR duration & capacitor use")
|
||||
elif buffId == 15: # Armor Burst: Armor Reinforcement: Armor HP
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("armor HP")
|
||||
elif buffId == 16: # Information Burst: Sensor Optimization: Scan Resolution
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("scan resolution")
|
||||
elif buffId == 26: # Information Burst: Sensor Optimization: Targeting Range
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("targeting range")
|
||||
elif buffId == 17: # Information Burst: Electronic Superiority: EWAR Range and Strength
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("electronic warfare modules range & strength")
|
||||
elif buffId == 18: # Information Burst: Electronic Hardening: Sensor Strength
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("sensor strength")
|
||||
elif buffId == 19: # Information Burst: Electronic Hardening: RSD/RWD Resistance
|
||||
textSections.append(f"{formatAmount(-buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("sensor dampener & weapon disruption resistance")
|
||||
elif buffId == 20: # Skirmish Burst: Evasive Maneuvers: Signature Radius
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("signature radius")
|
||||
elif buffId == 60: # Skirmish Burst: Evasive Maneuvers: Agility
|
||||
# minus the buff value because we want Agility as shown ingame, not inertia modifier
|
||||
textSections.append(f"{formatAmount(-buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("agility")
|
||||
elif buffId == 21: # Skirmish Burst: Interdiction Maneuvers: Tackle Range
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("warp disruption & stasis web range")
|
||||
elif buffId == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("AB/MWD speed increase")
|
||||
elif buffId == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("mining/survey module range")
|
||||
elif buffId == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("mining module duration & capacitor use")
|
||||
elif buffId == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
|
||||
textSections.append(f"{formatAmount(buffValue, 3, 0, 3, forceSign=True)}%")
|
||||
tooltipSections.append("mining crystal volatility")
|
||||
if not textSections:
|
||||
return '', None
|
||||
text = ' | '.join(textSections)
|
||||
tooltip = '{} bonus'.format(' | '.join(tooltipSections))
|
||||
if tooltip:
|
||||
tooltip = tooltip[0].capitalize() + tooltip[1:]
|
||||
return text, tooltip
|
||||
elif stuff.charge is not None:
|
||||
chargeGroup = stuff.charge.group.name
|
||||
if chargeGroup.endswith("Rocket") or chargeGroup.endswith("Missile") or chargeGroup.endswith("Torpedo"):
|
||||
|
||||
@@ -42,6 +42,7 @@ from gui.contextMenu import ContextMenu
|
||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||
from service.character import Character
|
||||
from service.esi import Esi
|
||||
from service.esiAccess import APIException
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
@@ -888,14 +889,7 @@ class APIView(wx.Panel):
|
||||
def fetchCallback(e=None):
|
||||
if e:
|
||||
pyfalog.warn("Error fetching skill information for character for __fetchCallback")
|
||||
exc_type, exc_value, exc_trace = e
|
||||
if config.debug:
|
||||
exc_value = ''.join(traceback.format_exception(exc_type, exc_value, exc_trace))
|
||||
pyfalog.warn(exc_value)
|
||||
|
||||
wx.MessageBox(
|
||||
_t("Error fetching skill information"),
|
||||
_t("Error"), wx.ICON_ERROR | wx.STAY_ON_TOP)
|
||||
SkillFetchExceptionHandler(e)
|
||||
else:
|
||||
wx.MessageBox(
|
||||
_t("Successfully fetched skills"), _t("Success"), wx.ICON_INFORMATION | wx.STAY_ON_TOP)
|
||||
@@ -926,3 +920,24 @@ class SecStatusDialog(wx.Dialog):
|
||||
self.Layout()
|
||||
|
||||
self.Center(wx.BOTH)
|
||||
|
||||
|
||||
class SkillFetchExceptionHandler:
|
||||
def __init__(self, e):
|
||||
from gui.esiFittings import ESIExceptionHandler
|
||||
exc_type, exc_value, exc_trace = e
|
||||
if config.debug:
|
||||
exc_value = ''.join(traceback.format_exception(exc_type, exc_value, exc_trace))
|
||||
pyfalog.warn(exc_value)
|
||||
|
||||
try:
|
||||
try:
|
||||
raise exc_value
|
||||
except APIException as ex:
|
||||
pyfalog.error(ex)
|
||||
ESIExceptionHandler(ex)
|
||||
except Exception as ex:
|
||||
pyfalog.error(ex)
|
||||
wx.MessageBox(
|
||||
_t("Error fetching skill information"),
|
||||
_t("Error"), wx.ICON_ERROR | wx.STAY_ON_TOP)
|
||||
|
||||
@@ -163,15 +163,9 @@ class CharacterSelection(wx.Panel):
|
||||
if e is None:
|
||||
self.refreshCharacterList()
|
||||
else:
|
||||
from gui.characterEditor import SkillFetchExceptionHandler
|
||||
pyfalog.warn("Error fetching skill information for character for refreshAPICallback")
|
||||
exc_type, exc_value, exc_trace = e
|
||||
if config.debug:
|
||||
exc_value = ''.join(traceback.format_exception(exc_type, exc_value, exc_trace))
|
||||
pyfalog.warn(exc_value)
|
||||
|
||||
wx.MessageBox(
|
||||
_t("Error fetching skill information"),
|
||||
_t("Error"), wx.ICON_ERROR | wx.STAY_ON_TOP)
|
||||
SkillFetchExceptionHandler(e)
|
||||
|
||||
def charChanged(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
|
||||
@@ -9,6 +9,7 @@ import config
|
||||
import gui.globalEvents as GE
|
||||
from eos.db import getItem
|
||||
from eos.saveddata.cargo import Cargo
|
||||
import gui.mainFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.display import Display
|
||||
from gui.characterEditor import APIView
|
||||
@@ -132,7 +133,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
except APIException as ex:
|
||||
# Can't do this in a finally because then it obscures the message dialog
|
||||
del waitDialog # noqa: F821
|
||||
ESIExceptionHandler(self, ex)
|
||||
ESIExceptionHandler(ex)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as ex:
|
||||
@@ -149,6 +150,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
self.mainFrame._openAfterImport(fits)
|
||||
|
||||
def deleteFitting(self, event):
|
||||
self.statusbar.SetStatusText("")
|
||||
sEsi = Esi.getInstance()
|
||||
selection = self.fitView.fitSelection
|
||||
if not selection:
|
||||
@@ -156,7 +158,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
data = json.loads(self.fitTree.fittingsTreeCtrl.GetItemData(selection))
|
||||
|
||||
with wx.MessageDialog(
|
||||
self, _t("Do you really want to delete {} ({}) from EVE?").format((data['name'], getItem(data['ship_type_id']).name)),
|
||||
self, _t("Do you really want to delete {} ({}) from EVE?").format(data['name'], getItem(data['ship_type_id']).name),
|
||||
_t("Confirm Delete"), wx.YES | wx.NO | wx.ICON_QUESTION
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
@@ -164,16 +166,26 @@ class EveFittings(AuxiliaryFrame):
|
||||
if activeChar is None:
|
||||
return
|
||||
try:
|
||||
sEsi.delFitting(activeChar, data['fitting_id'])
|
||||
# repopulate the fitting list
|
||||
self.fitTree.populateSkillTree(self.fittings)
|
||||
self.fitView.update([])
|
||||
try:
|
||||
sEsi.delFitting(activeChar, data['fitting_id'])
|
||||
# repopulate the fitting list
|
||||
self.fitTree.populateSkillTree(self.fittings)
|
||||
self.fitView.update([])
|
||||
except APIException as ex:
|
||||
pyfalog.error(ex)
|
||||
self.statusbar.SetStatusText("Failed to delete fit: ESI error {} received - {}".format(ex.status_code, ex.response["error"]))
|
||||
try:
|
||||
ESIExceptionHandler(ex)
|
||||
except:
|
||||
# don't need to do anything - we should already have error code in the status
|
||||
pass
|
||||
except requests.exceptions.ConnectionError:
|
||||
msg = _t("Connection error, please check your internet connection")
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
|
||||
def deleteAllFittings(self, event):
|
||||
self.statusbar.SetStatusText("")
|
||||
sEsi = Esi.getInstance()
|
||||
activeChar = self.getActiveCharacter()
|
||||
if activeChar is None:
|
||||
@@ -186,20 +198,30 @@ class EveFittings(AuxiliaryFrame):
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
try:
|
||||
for fit in self.fittings:
|
||||
sEsi.delFitting(activeChar, fit['fitting_id'])
|
||||
anyDeleted = True
|
||||
try:
|
||||
for fit in self.fittings:
|
||||
sEsi.delFitting(activeChar, fit['fitting_id'])
|
||||
anyDeleted = True
|
||||
except APIException as ex:
|
||||
pyfalog.error(ex)
|
||||
if anyDeleted:
|
||||
msg = "Some fits were not deleted: ESI error {} received - {}".format(ex.status_code,
|
||||
ex.response["error"])
|
||||
else:
|
||||
msg = "Failed to delete fits: ESI error {} received - {}".format(ex.status_code,
|
||||
ex.response["error"])
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
try:
|
||||
ESIExceptionHandler(ex)
|
||||
except:
|
||||
# don't need to do anything - we should already have error code in the status
|
||||
pass
|
||||
except requests.exceptions.ConnectionError:
|
||||
msg = "Connection error, please check your internet connection"
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
except APIException as ex:
|
||||
if anyDeleted:
|
||||
msg = "Some fits were not deleted: ESI error {} received".format(ex.status_code)
|
||||
else:
|
||||
msg = "Failed to delete fits: ESI error {} received".format(ex.status_code)
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
|
||||
# repopulate the fitting list
|
||||
self.fitTree.populateSkillTree(self.fittings)
|
||||
self.fitView.update([])
|
||||
@@ -222,15 +244,33 @@ class ESIServerExceptionHandler:
|
||||
|
||||
class ESIExceptionHandler:
|
||||
# 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
|
||||
def __init__(self, ex):
|
||||
# raise ex
|
||||
if ex.response['error'].startswith('Token is not valid') \
|
||||
or ex.response['error'] == 'invalid_token' \
|
||||
or ex.response['error'] == 'invalid_grant': # todo: this seems messy, figure out a better response
|
||||
pyfalog.error(ex)
|
||||
with wx.MessageDialog(
|
||||
parentWindow,
|
||||
gui.mainFrame.MainFrame.getInstance(),
|
||||
_t("There was an error validating characters' SSO token. Please try "
|
||||
"logging into the character again to reset the token."),
|
||||
_t("Invalid Token"),
|
||||
wx.OK | wx.ICON_ERROR
|
||||
wx.OK | wx.ICON_ERROR | wx.CANCEL
|
||||
) as dlg:
|
||||
dlg.SetOKLabel("Manage ESI Characters")
|
||||
ret = dlg.ShowModal()
|
||||
if ret == wx.ID_OK:
|
||||
SsoCharacterMgmt.openOne(parent=gui.mainFrame.MainFrame.getInstance())
|
||||
# todo: spawn manage esi characters
|
||||
pass
|
||||
elif ex.response['error'].startswith('Timeout contacting'):
|
||||
pyfalog.error(ex)
|
||||
with wx.MessageDialog(
|
||||
gui.mainFrame.MainFrame.getInstance(),
|
||||
"HTTP %s: %s\n\n" % (ex.status_code, ex.response['error'])
|
||||
+ _t("The server took too long to response. Please try again in a moment."),
|
||||
_t("Timeout"),
|
||||
wx.OK | wx.ICON_ERROR
|
||||
) as dlg:
|
||||
dlg.ShowModal()
|
||||
else:
|
||||
@@ -335,9 +375,9 @@ class ExportToEve(AuxiliaryFrame):
|
||||
pyfalog.warning(msg)
|
||||
self.statusbar.SetStatusText(msg, 1)
|
||||
return
|
||||
res = sEsi.postFitting(activeChar, data)
|
||||
|
||||
try:
|
||||
res = sEsi.postFitting(activeChar, data)
|
||||
res.raise_for_status()
|
||||
self.statusbar.SetStatusText("", 0)
|
||||
self.statusbar.SetStatusText(res.reason, 1)
|
||||
@@ -346,19 +386,19 @@ class ExportToEve(AuxiliaryFrame):
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(_t("ERROR"), 0)
|
||||
self.statusbar.SetStatusText(msg, 1)
|
||||
except ESIExportException as ex:
|
||||
except APIException as ex:
|
||||
pyfalog.error(ex)
|
||||
self.statusbar.SetStatusText(_t("ERROR"), 0)
|
||||
self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
|
||||
except APIException as ex:
|
||||
self.statusbar.SetStatusText("HTTP {} - {}".format(ex.status_code, ex.response["error"]), 1)
|
||||
try:
|
||||
ESIExceptionHandler(self, ex)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as ex:
|
||||
self.statusbar.SetStatusText(_t("ERROR"), 0)
|
||||
self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
|
||||
pyfalog.error(ex)
|
||||
ESIExceptionHandler(ex)
|
||||
except:
|
||||
# don't need to do anything - we should already get the error in ex.response
|
||||
pass
|
||||
except Exception as ex:
|
||||
self.statusbar.SetStatusText(_t("ERROR"), 0)
|
||||
self.statusbar.SetStatusText("Unknown error", 1)
|
||||
pyfalog.error(ex)
|
||||
|
||||
|
||||
class SsoCharacterMgmt(AuxiliaryFrame):
|
||||
|
||||
@@ -5,7 +5,7 @@ from .gui.booster.remove import GuiRemoveBoostersCommand
|
||||
from .gui.booster.sideEffectToggleState import GuiToggleBoosterSideEffectStateCommand
|
||||
from .gui.booster.toggleStates import GuiToggleBoosterStatesCommand
|
||||
from .gui.cargo.add import GuiAddCargoCommand
|
||||
from .gui.cargo.changeAmount import GuiChangeCargoAmountCommand
|
||||
from .gui.cargo.changeAmount import GuiChangeCargosAmountCommand
|
||||
from .gui.cargo.changeMetas import GuiChangeCargoMetasCommand
|
||||
from .gui.cargo.imprt import GuiImportCargosCommand
|
||||
from .gui.cargo.remove import GuiRemoveCargosCommand
|
||||
@@ -26,8 +26,12 @@ from .gui.itemsRebase import GuiRebaseItemsCommand
|
||||
from .gui.localDrone.add import GuiAddLocalDroneCommand
|
||||
from .gui.localDrone.changeAmount import GuiChangeLocalDroneAmountCommand
|
||||
from .gui.localDrone.changeMetas import GuiChangeLocalDroneMetasCommand
|
||||
from .gui.localDrone.changeMutation import GuiChangeLocalDroneMutationCommand
|
||||
from .gui.localDrone.clone import GuiCloneLocalDroneCommand
|
||||
from .gui.localDrone.imprt import GuiImportLocalDronesCommand
|
||||
from .gui.localDrone.mutatedConvert import GuiConvertMutatedLocalDroneCommand
|
||||
from .gui.localDrone.mutatedImport import GuiImportLocalMutatedDroneCommand
|
||||
from .gui.localDrone.mutatedRevert import GuiRevertMutatedLocalDroneCommand
|
||||
from .gui.localDrone.remove import GuiRemoveLocalDronesCommand
|
||||
from .gui.localDrone.stackSplit import GuiSplitLocalDroneStackCommand
|
||||
from .gui.localDrone.stacksMerge import GuiMergeLocalDroneStacksCommand
|
||||
|
||||
51
gui/fitCommands/calc/drone/localChangeMutation.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class CalcChangeLocalDroneMutationCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position, mutation, oldMutation=None):
|
||||
wx.Command.__init__(self, True, 'Change Local Drone Mutation')
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
self.mutation = mutation
|
||||
self.savedMutation = oldMutation
|
||||
|
||||
def Do(self):
|
||||
pyfalog.debug('Doing changing of local drone mutation at position {} to {} for fit ID {}'.format(
|
||||
self.position, self.mutation, self.fitID))
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
drone = fit.drones[self.position]
|
||||
if not drone.isMutated:
|
||||
return False
|
||||
|
||||
if self.savedMutation is None:
|
||||
self.savedMutation = {}
|
||||
for mutator in drone.mutators.values():
|
||||
self.savedMutation[mutator.attrID] = mutator.value
|
||||
|
||||
if self.mutation == self.savedMutation:
|
||||
return False
|
||||
|
||||
for mutator in drone.mutators.values():
|
||||
if mutator.attrID not in self.mutation:
|
||||
continue
|
||||
if mutator.value != self.mutation[mutator.attrID]:
|
||||
mutator.value = self.mutation[mutator.attrID]
|
||||
|
||||
return True
|
||||
|
||||
def Undo(self):
|
||||
pyfalog.debug('Undoing changing of local drone mutation at position {} to {} for fit ID {}'.format(
|
||||
self.position, self.mutation, self.fitID))
|
||||
cmd = CalcChangeLocalDroneMutationCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
mutation=self.savedMutation)
|
||||
return cmd.Do()
|
||||
@@ -10,21 +10,26 @@ from gui.fitCommands.calc.cargo.remove import CalcRemoveCargoCommand
|
||||
from gui.fitCommands.helpers import CargoInfo, InternalCommandHistory
|
||||
|
||||
|
||||
class GuiChangeCargoAmountCommand(wx.Command):
|
||||
class GuiChangeCargosAmountCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, itemID, amount):
|
||||
def __init__(self, fitID, itemIDs, amount):
|
||||
wx.Command.__init__(self, True, 'Change Cargo Amount')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.itemID = itemID
|
||||
self.itemIDs = itemIDs
|
||||
self.amount = amount
|
||||
|
||||
def Do(self):
|
||||
results = []
|
||||
if self.amount > 0:
|
||||
cmd = CalcChangeCargoAmountCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=self.itemID, amount=self.amount))
|
||||
for itemID in self.itemIDs:
|
||||
cmd = CalcChangeCargoAmountCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=itemID, amount=self.amount))
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
else:
|
||||
cmd = CalcRemoveCargoCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=self.itemID, amount=math.inf))
|
||||
success = self.internalHistory.submit(cmd)
|
||||
for itemID in self.itemIDs:
|
||||
cmd = CalcRemoveCargoCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=itemID, amount=math.inf))
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
success = any(results)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
|
||||
@@ -14,7 +14,7 @@ class GuiImportCargosCommand(wx.Command):
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.cargos = {}
|
||||
for itemID, amount in cargos:
|
||||
for itemID, amount, mutation in cargos:
|
||||
if itemID not in self.cargos:
|
||||
self.cargos[itemID] = 0
|
||||
self.cargos[itemID] += amount
|
||||
|
||||
44
gui/fitCommands/gui/localDrone/changeMutation.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import wx
|
||||
|
||||
import eos.db
|
||||
import gui.mainFrame
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.localChangeMutation import CalcChangeLocalDroneMutationCommand
|
||||
from gui.fitCommands.helpers import InternalCommandHistory
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class GuiChangeLocalDroneMutationCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position, mutation, oldMutation=None):
|
||||
wx.Command.__init__(self, True, 'Change Local Drone Mutation')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
self.mutation = mutation
|
||||
self.oldMutation = oldMutation
|
||||
|
||||
def Do(self):
|
||||
cmd = CalcChangeLocalDroneMutationCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
mutation=self.mutation,
|
||||
oldMutation=self.oldMutation)
|
||||
success = self.internalHistory.submit(cmd)
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
|
||||
def Undo(self):
|
||||
success = self.internalHistory.undoAll()
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
@@ -14,15 +14,24 @@ class GuiImportLocalDronesCommand(wx.Command):
|
||||
wx.Command.__init__(self, True, 'Import Local Drones')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.drones = drones
|
||||
self.droneInfos = []
|
||||
for itemID, amount, mutation in drones:
|
||||
if mutation:
|
||||
mutaplasmid, attrs = mutation
|
||||
self.droneInfos.append(DroneInfo(
|
||||
itemID=mutaplasmid.resultingItem.ID,
|
||||
amount=amount,
|
||||
amountActive=0,
|
||||
baseItemID=itemID,
|
||||
mutaplasmidID=mutaplasmid.ID,
|
||||
mutations=attrs))
|
||||
else:
|
||||
self.droneInfos.append(DroneInfo(itemID=itemID, amount=amount, amountActive=0))
|
||||
|
||||
def Do(self):
|
||||
results = []
|
||||
for itemID, amount in self.drones:
|
||||
cmd = CalcAddLocalDroneCommand(
|
||||
fitID=self.fitID,
|
||||
droneInfo=DroneInfo(itemID=itemID, amount=amount, amountActive=0),
|
||||
forceNewStack=True)
|
||||
for info in self.droneInfos:
|
||||
cmd = CalcAddLocalDroneCommand(fitID=self.fitID, droneInfo=info, forceNewStack=True)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
success = any(results)
|
||||
eos.db.flush()
|
||||
|
||||
65
gui/fitCommands/gui/localDrone/mutatedConvert.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import math
|
||||
|
||||
import wx
|
||||
|
||||
import eos.db
|
||||
import gui.mainFrame
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
|
||||
from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
|
||||
from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class GuiConvertMutatedLocalDroneCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position, mutaplasmid):
|
||||
wx.Command.__init__(self, True, 'Convert Local Drone to Mutated')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
self.itemID = mutaplasmid.resultingItem.ID
|
||||
self.mutaplasmidID = mutaplasmid.ID
|
||||
|
||||
def Do(self):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
try:
|
||||
drone = fit.drones[self.position]
|
||||
except IndexError:
|
||||
return False
|
||||
if drone.isMutated:
|
||||
return False
|
||||
info = DroneInfo(
|
||||
amount=drone.amount,
|
||||
amountActive=drone.amountActive,
|
||||
itemID=self.itemID,
|
||||
baseItemID=drone.item.ID,
|
||||
mutaplasmidID=self.mutaplasmidID,
|
||||
mutations={})
|
||||
cmdRemove = CalcRemoveLocalDroneCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
amount=math.inf)
|
||||
cmdAdd = CalcAddLocalDroneCommand(
|
||||
fitID=self.fitID,
|
||||
droneInfo=info,
|
||||
forceNewStack=True,
|
||||
ignoreRestrictions=True)
|
||||
success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
|
||||
eos.db.flush()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
|
||||
def Undo(self):
|
||||
success = self.internalHistory.undoAll()
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
44
gui/fitCommands/gui/localDrone/mutatedImport.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import wx
|
||||
|
||||
import eos.db
|
||||
import gui.mainFrame
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
|
||||
from gui.fitCommands.helpers import InternalCommandHistory, DroneInfo
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class GuiImportLocalMutatedDroneCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, baseItem, mutaplasmid, mutations, amount):
|
||||
wx.Command.__init__(self, True, 'Import Local Mutated Drone')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.newDroneInfo = DroneInfo(
|
||||
amount=amount,
|
||||
amountActive=0,
|
||||
itemID=mutaplasmid.resultingItem.ID,
|
||||
baseItemID=baseItem.ID,
|
||||
mutaplasmidID=mutaplasmid.ID,
|
||||
mutations=mutations)
|
||||
|
||||
def Do(self):
|
||||
cmd = CalcAddLocalDroneCommand(fitID=self.fitID, droneInfo=self.newDroneInfo, forceNewStack=True)
|
||||
success = self.internalHistory.submit(cmd)
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
|
||||
def Undo(self):
|
||||
success = self.internalHistory.undoAll()
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
60
gui/fitCommands/gui/localDrone/mutatedRevert.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import math
|
||||
|
||||
import wx
|
||||
|
||||
import eos.db
|
||||
import gui.mainFrame
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
|
||||
from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
|
||||
from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class GuiRevertMutatedLocalDroneCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position):
|
||||
wx.Command.__init__(self, True, 'Revert Local Drone from Mutated')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
|
||||
def Do(self):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
try:
|
||||
drone = fit.drones[self.position]
|
||||
except IndexError:
|
||||
return False
|
||||
if not drone.isMutated:
|
||||
return False
|
||||
info = DroneInfo(
|
||||
amount=drone.amount,
|
||||
amountActive=drone.amountActive,
|
||||
itemID=drone.baseItemID)
|
||||
cmdRemove = CalcRemoveLocalDroneCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
amount=math.inf)
|
||||
cmdAdd = CalcAddLocalDroneCommand(
|
||||
fitID=self.fitID,
|
||||
droneInfo=info,
|
||||
forceNewStack=True,
|
||||
ignoreRestrictions=True)
|
||||
success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
|
||||
eos.db.flush()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
|
||||
def Undo(self):
|
||||
success = self.internalHistory.undoAll()
|
||||
eos.db.flush()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.recalc(self.fitID)
|
||||
sFit.fill(self.fitID)
|
||||
eos.db.commit()
|
||||
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
|
||||
return success
|
||||
@@ -14,7 +14,7 @@ class GuiImportLocalFightersCommand(wx.Command):
|
||||
wx.Command.__init__(self, True, 'Import Local Fighters')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.fighters = fighters
|
||||
self.fighters = [(i, a) for i, a, m in fighters]
|
||||
|
||||
def Do(self):
|
||||
results = []
|
||||
|
||||
@@ -21,7 +21,10 @@ class GuiConvertMutatedLocalModuleCommand(wx.Command):
|
||||
def Do(self):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
mod = fit.modules[self.position]
|
||||
try:
|
||||
mod = fit.modules[self.position]
|
||||
except IndexError:
|
||||
return False
|
||||
if mod.isEmpty:
|
||||
return False
|
||||
if mod.isMutated:
|
||||
|
||||
@@ -19,7 +19,10 @@ class GuiRevertMutatedLocalModuleCommand(wx.Command):
|
||||
def Do(self):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
mod = fit.modules[self.position]
|
||||
try:
|
||||
mod = fit.modules[self.position]
|
||||
except IndexError:
|
||||
return False
|
||||
if mod.isEmpty:
|
||||
return False
|
||||
if not mod.isMutated:
|
||||
|
||||
@@ -158,8 +158,11 @@ class ModuleInfo:
|
||||
|
||||
class DroneInfo:
|
||||
|
||||
def __init__(self, itemID, amount, amountActive):
|
||||
def __init__(self, amount, amountActive, itemID, baseItemID=None, mutaplasmidID=None, mutations=None):
|
||||
self.itemID = itemID
|
||||
self.baseItemID = baseItemID
|
||||
self.mutaplasmidID = mutaplasmidID
|
||||
self.mutations = mutations
|
||||
self.amount = amount
|
||||
self.amountActive = amountActive
|
||||
|
||||
@@ -170,22 +173,40 @@ class DroneInfo:
|
||||
info = cls(
|
||||
itemID=drone.itemID,
|
||||
amount=drone.amount,
|
||||
amountActive=drone.amountActive)
|
||||
amountActive=drone.amountActive,
|
||||
baseItemID=drone.baseItemID,
|
||||
mutaplasmidID=drone.mutaplasmidID,
|
||||
mutations={m.attrID: m.value for m in drone.mutators.values()})
|
||||
return info
|
||||
|
||||
def toDrone(self):
|
||||
item = Market.getInstance().getItem(self.itemID, eager=('attributes', 'group.category'))
|
||||
mkt = Market.getInstance()
|
||||
item = mkt.getItem(self.itemID, eager=('attributes', 'group.category'))
|
||||
if self.baseItemID and self.mutaplasmidID:
|
||||
baseItem = mkt.getItem(self.baseItemID, eager=('attributes', 'group.category'))
|
||||
mutaplasmid = eos.db.getDynamicItem(self.mutaplasmidID)
|
||||
else:
|
||||
baseItem = None
|
||||
mutaplasmid = None
|
||||
try:
|
||||
drone = Drone(item)
|
||||
drone = Drone(item, baseItem=baseItem, mutaplasmid=mutaplasmid)
|
||||
except ValueError:
|
||||
pyfalog.warning('Invalid item: {}'.format(self.itemID))
|
||||
return None
|
||||
|
||||
if self.mutations is not None:
|
||||
for attrID, mutator in drone.mutators.items():
|
||||
if attrID in self.mutations:
|
||||
mutator.value = self.mutations[attrID]
|
||||
|
||||
drone.amount = self.amount
|
||||
drone.amountActive = self.amountActive
|
||||
return drone
|
||||
|
||||
def __repr__(self):
|
||||
return makeReprStr(self, ['itemID', 'amount', 'amountActive'])
|
||||
return makeReprStr(self, [
|
||||
'itemID', 'amount', 'amountActive',
|
||||
'baseItemID', 'mutaplasmidID', 'mutations'])
|
||||
|
||||
|
||||
class FighterInfo:
|
||||
@@ -325,7 +346,8 @@ def activeStateLimit(itemIdentity):
|
||||
'cynosuralGeneration', 'jumpPortalGeneration', 'jumpPortalGenerationBO',
|
||||
'cloneJumpAccepting', 'cloakingWarpSafe', 'cloakingPrototype', 'cloaking',
|
||||
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively',
|
||||
'cargoScan', 'shipScan', 'surveyScan'
|
||||
'cargoScan', 'shipScan', 'surveyScan', 'targetSpectrumBreakerBonus',
|
||||
'interdictionNullifierBonus', 'warpCoreStabilizerActive'
|
||||
}.intersection(item.effects):
|
||||
return FittingModuleState.ONLINE
|
||||
return FittingModuleState.ACTIVE
|
||||
|
||||
@@ -22,6 +22,7 @@ import wx
|
||||
|
||||
import config
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.module import Module
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
@@ -165,7 +166,7 @@ class ItemStatsContainer(wx.Panel):
|
||||
self.traits = ItemTraits(self.nbContainer, stuff, item)
|
||||
self.nbContainer.AddPage(self.traits, _t("Traits"))
|
||||
|
||||
if isinstance(stuff, Module) and stuff.isMutated:
|
||||
if isinstance(stuff, (Module, Drone)) and stuff.isMutated:
|
||||
self.mutator = ItemMutatorPanel(self.nbContainer, stuff)
|
||||
self.nbContainer.AddPage(self.mutator, _t("Mutations"))
|
||||
|
||||
|
||||
@@ -762,28 +762,33 @@ class MainFrame(wx.Frame):
|
||||
if importType == "FittingItem":
|
||||
baseItem, mutaplasmidItem, mutations = importData[0]
|
||||
if mutaplasmidItem:
|
||||
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(activeFit, baseItem, mutaplasmidItem, mutations))
|
||||
if baseItem.isDrone:
|
||||
self.command.Submit(cmd.GuiImportLocalMutatedDroneCommand(
|
||||
activeFit, baseItem, mutaplasmidItem, mutations, amount=1))
|
||||
else:
|
||||
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(
|
||||
activeFit, baseItem, mutaplasmidItem, mutations))
|
||||
else:
|
||||
self.command.Submit(cmd.GuiAddLocalModuleCommand(activeFit, baseItem.ID))
|
||||
return
|
||||
if importType == "AdditionsDrones":
|
||||
if self.command.Submit(cmd.GuiImportLocalDronesCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
|
||||
if self.command.Submit(cmd.GuiImportLocalDronesCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
|
||||
self.additionsPane.select("Drones")
|
||||
return
|
||||
if importType == "AdditionsFighters":
|
||||
if self.command.Submit(cmd.GuiImportLocalFightersCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
|
||||
if self.command.Submit(cmd.GuiImportLocalFightersCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
|
||||
self.additionsPane.select("Fighters")
|
||||
return
|
||||
if importType == "AdditionsImplants":
|
||||
if self.command.Submit(cmd.GuiImportImplantsCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
|
||||
if self.command.Submit(cmd.GuiImportImplantsCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
|
||||
self.additionsPane.select("Implants")
|
||||
return
|
||||
if importType == "AdditionsBoosters":
|
||||
if self.command.Submit(cmd.GuiImportBoostersCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
|
||||
if self.command.Submit(cmd.GuiImportBoostersCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
|
||||
self.additionsPane.select("Boosters")
|
||||
return
|
||||
if importType == "AdditionsCargo":
|
||||
if self.command.Submit(cmd.GuiImportCargosCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
|
||||
if self.command.Submit(cmd.GuiImportCargosCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
|
||||
self.additionsPane.select("Cargo")
|
||||
return
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
|
||||
@@ -38,7 +38,7 @@ class SsoLogin(wx.Dialog):
|
||||
from service.esi import Esi
|
||||
|
||||
self.sEsi = Esi.getInstance()
|
||||
uri = self.sEsi.getLoginURI(None)
|
||||
uri = self.sEsi.get_login_uri(None)
|
||||
webbrowser.open(uri)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class SsoLoginServer(wx.Dialog):
|
||||
self.sEsi = Esi.getInstance()
|
||||
serverAddr = self.sEsi.startServer(port)
|
||||
|
||||
uri = self.sEsi.getLoginURI(serverAddr)
|
||||
uri = self.sEsi.get_login_uri(serverAddr)
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
|
||||
|
||||
BIN
imgs/icons/10065@1x.png
Normal file
|
After Width: | Height: | Size: 658 B |
BIN
imgs/icons/10065@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 683 B |
|
Before Width: | Height: | Size: 1.7 KiB |
BIN
imgs/icons/1157@1x.png
Normal file
|
After Width: | Height: | Size: 888 B |
BIN
imgs/icons/1157@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/1433@1x.png
Normal file
|
After Width: | Height: | Size: 701 B |
BIN
imgs/icons/1433@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/21911@1x.png
Normal file
|
After Width: | Height: | Size: 756 B |
BIN
imgs/icons/21911@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/21912@1x.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
imgs/icons/21912@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/21914@1x.png
Normal file
|
After Width: | Height: | Size: 769 B |
BIN
imgs/icons/21914@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 823 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 747 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 735 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 680 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 739 B After Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 702 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 880 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 864 B After Width: | Height: | Size: 866 B |