Compare commits
93 Commits
v2.11.0
...
v2.13.1dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e70ebad3f7 | ||
|
|
4dfe5c3abf | ||
|
|
536eb1efa5 | ||
|
|
c4c763089e | ||
|
|
cdfd4c0d8e | ||
|
|
f9bb8836e5 | ||
|
|
58b2634c8c | ||
|
|
093ae008ce | ||
|
|
5f62fc0cdc | ||
|
|
e7a4b4ac26 | ||
|
|
66e9944cb5 | ||
|
|
ad8528c248 | ||
|
|
07d22cd8e4 | ||
|
|
a6d5922d77 | ||
|
|
958d7bff99 | ||
|
|
7819b80be4 | ||
|
|
2ca50a4658 | ||
|
|
09ff4fd128 | ||
|
|
3e53863f9e | ||
|
|
63d2289f97 | ||
|
|
2663ef2e66 | ||
|
|
d4bdf47d62 | ||
|
|
660ee7c4bf | ||
|
|
1db1f3070b | ||
|
|
3dba82c497 | ||
|
|
25e7b7a9f7 | ||
|
|
9582212ae0 | ||
|
|
7d2b60c327 | ||
|
|
0121a0064e | ||
|
|
2aa96fc819 | ||
|
|
8d81db0a3a | ||
|
|
e5ba35fde9 | ||
|
|
885cd32cb0 | ||
|
|
18d8ed6558 | ||
|
|
9618ece4b4 | ||
|
|
a80a77a422 | ||
|
|
3806be3ddd | ||
|
|
3e803fef30 | ||
|
|
12956d435a | ||
|
|
a3381007f3 | ||
|
|
1efe4ee5e5 | ||
|
|
ec21f93d3c | ||
|
|
f384b32ed6 | ||
|
|
22d8f34c75 | ||
|
|
6128cd8322 | ||
|
|
386f403430 | ||
|
|
5f7d9aea89 | ||
|
|
b367c449a9 | ||
|
|
26b3dff9d4 | ||
|
|
873a62e3f0 | ||
|
|
d967ab375e | ||
|
|
fcf2d6a72c | ||
|
|
843ced15bf | ||
|
|
813db9340f | ||
|
|
acbd8a3298 | ||
|
|
561e22e894 | ||
|
|
05ac0a528a | ||
|
|
c040353f6e | ||
|
|
f23a8fa0c8 | ||
|
|
ba93467646 | ||
|
|
00d480860f | ||
|
|
c94384acb8 | ||
|
|
0c2c0ac6ef | ||
|
|
61a33a331e | ||
|
|
e374a6f2c6 | ||
|
|
dbd84dce28 | ||
|
|
9d554f9c68 | ||
|
|
576cf56735 | ||
|
|
e2aaabbc16 | ||
|
|
ef226898c0 | ||
|
|
a0db235e5a | ||
|
|
5bf05ba775 | ||
|
|
c073b1fa2a | ||
|
|
5f58307bf3 | ||
|
|
8741b17a5e | ||
|
|
4c1fa09795 | ||
|
|
ce7df2d01f | ||
|
|
b433b0ea7c | ||
|
|
20868d6b44 | ||
|
|
33103dbee9 | ||
|
|
2a05ac5a85 | ||
|
|
a013828128 | ||
|
|
e19510b3d4 | ||
|
|
390f2048f2 | ||
|
|
0bb732300e | ||
|
|
fd017df561 | ||
|
|
0ed16b9a6f | ||
|
|
865978fcc1 | ||
|
|
a43f9930de | ||
|
|
c13cd23d54 | ||
|
|
ed1f52a114 | ||
|
|
7dd063f04e | ||
|
|
6e9fc1d1d9 |
@@ -52,8 +52,8 @@ install:
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- ECHO "Install pip requirements:"
|
||||
- "pip install -r requirements.txt"
|
||||
- "pip install PyInstaller"
|
||||
- "python -m pip install -r requirements.txt"
|
||||
- "python -m pip install PyInstaller"
|
||||
|
||||
before_build:
|
||||
# directory that will contain the built files
|
||||
|
||||
79
CONTRIBUTING.md
Normal file
79
CONTRIBUTING.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Contribution
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.6
|
||||
- Git CLI installed
|
||||
- Python, pip and git are all available as command-line commands (add to path if needed)
|
||||
|
||||
Virtual environment will be created in *PyfaEnv* folder. Project will be cloned and run from the *PyfaDEV* folder. Separate virtual environment will be created so required libraries won't clutter the main python installation.
|
||||
|
||||
> Commands and screens were created on Windows 10. Please, update all the paths according to your OS.
|
||||
|
||||
## Setting up the project manually
|
||||
|
||||
Clone the repo
|
||||
```
|
||||
git clone <repo> PyfaDEV
|
||||
```
|
||||
|
||||
Create virtual environment
|
||||
```
|
||||
python -m venv PyfaEnv
|
||||
```
|
||||
|
||||
Activate virtual environment
|
||||
|
||||
```
|
||||
For cmd.exe: PyfaEnv\scripts\activate.bat
|
||||
For bash: source <venv>/bin/activate
|
||||
```
|
||||
> For other OS check [Python documentation](https://docs.python.org/3/library/venv.html)
|
||||
|
||||
Install requirements for the project from *requirements.txt*
|
||||
```
|
||||
pip install -r PyfaDEV\requirements.txt
|
||||
```
|
||||
> For some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx`
|
||||
|
||||
Check that libs from *requirements.txt* are installed
|
||||
```
|
||||
pip list
|
||||
```
|
||||
|
||||
Test that the project is starting properly
|
||||
```
|
||||
python PyfaDEV\pyfa.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Setting up the project with PyCharm/IntelliJ
|
||||
|
||||
Install PyCharm / Other IntelliJ product with Python plugin
|
||||
|
||||
After launching select *Check out from Version Control* -> *GIt*
|
||||
|
||||

|
||||
|
||||
Login to GitHub, paste repo URL and seect the folder to which to clone the project, press *Clone*.
|
||||
|
||||

|
||||
|
||||
After process is complete, open *File* -> *Settings* -> *Project* -> *Project Interpreter*.
|
||||
|
||||

|
||||
|
||||
Press on options and add new virtual environment.
|
||||
|
||||

|
||||
|
||||
Open project tree view and double-click on *requirements.txt*. Press *Install requirements*. Install all requirements.
|
||||
|
||||

|
||||
|
||||
Create new *Run Configuration*. Set correct *Script path* and *Python interpreter*.
|
||||
|
||||

|
||||
|
||||
Test that the project is starting properly.
|
||||
34
README.md
34
README.md
@@ -2,20 +2,19 @@
|
||||
|
||||
[](https://pyfainvite.azurewebsites.net/) [](https://travis-ci.org/pyfa-org/Pyfa)
|
||||
|
||||

|
||||

|
||||
|
||||
## What is it?
|
||||
|
||||
pyfa, short for **py**thon **f**itting **a**ssistant, allows you to create, experiment with, and save ship fittings without being in game. Open source and written in Python, it is available on any platform where Python 2.x and wxWidgets are available, including Windows, Mac OS X, and Linux.
|
||||
Pyfa, short for **py**thon **f**itting **a**ssistant, allows you to create, experiment with, and save ship fittings without being in game. Open source and written in Python, it is available on any platform where Python 3 and wxWidgets are available, including Windows, Mac OS X, and Linux.
|
||||
|
||||
## Latest Version and Changelogs
|
||||
The latest version along with release notes can always be found on the project's [Releases](https://github.com/DarkFenX/Pyfa/releases) page. pyfa will notify you if you are running an outdated version.
|
||||
The latest version along with release notes can always be found on the project's [releases](https://github.com/pyfa-org/Pyfa/releases) page. Pyfa will notify you if you are running an outdated version.
|
||||
|
||||
## Installation
|
||||
Windows and OS X users are supplied self-contained builds of pyfa on the [latest releases](https://github.com/pyfa-org/Pyfa/releases/latest) page. An `.exe` installer is also available for Windows builds. Linux users can run pyfa using their distribution's Python interpreter. There is no official self-contained package for Linux, however, there are a number of third-party packages available through distribution-specific repositories.
|
||||
|
||||
#### OS X
|
||||
|
||||
### OS X
|
||||
Apart from the official release, there is also a [Homebrew](http://brew.sh) option for installing pyfa on OS X. Please note this is maintained by a third-party and is not tested by pyfa developers. Simply fire up in terminal:
|
||||
```
|
||||
$ brew install Caskroom/cask/pyfa
|
||||
@@ -27,34 +26,31 @@ The following is a list of pyfa packages available for certain distributions. Pl
|
||||
* Arch: https://aur.archlinux.org/packages/pyfa/
|
||||
* Gentoo: https://github.com/ZeroPointEnergy/gentoo-pyfa-overlay
|
||||
|
||||
### Dependencies
|
||||
If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required:
|
||||
|
||||
* Python 3.6
|
||||
* Requirements as listed in `requirements.txt`
|
||||
## Contribution
|
||||
If you wish to help with development or you need to run pyfa through a Python interpreter, check out [the instructions](https://github.com/pyfa-org/Pyfa/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Bug Reporting
|
||||
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting).
|
||||
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](https://forums.eveonline.com/t/27156) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/pyfa-org/Pyfa/wiki/Bug-Reporting).
|
||||
|
||||
## License
|
||||
pyfa is licensed under the GNU GPL v3.0, see LICENSE
|
||||
Pyfa is licensed under the GNU GPL v3.0, see LICENSE
|
||||
|
||||
## Resources
|
||||
* Development repository: [https://github.com/pyfa-org/Pyfa](https://github.com/pyfa-org/Pyfa)
|
||||
* [Development repository](https://github.com/pyfa-org/Pyfa)
|
||||
* [EVE forum thread](https://forums.eveonline.com/t/27156)
|
||||
* [EVE University guide using pyfa](http://wiki.eveuniversity.org/Guide_to_using_PYFA)
|
||||
* [EVE University guide using pyfa](https://wiki.eveuniversity.org/PYFA)
|
||||
* [EVE Online website](http://www.eveonline.com/)
|
||||
|
||||
## Contacts:
|
||||
* Sable Blitzmann
|
||||
* GitHub: @blitzmann
|
||||
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
|
||||
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @ blitzmann
|
||||
* Email: sable.blitzmann@gmail.com
|
||||
* Kadesh / DarkPhoenix
|
||||
* GitHub: @DarkFenX
|
||||
* EVE: Kadesh Priestess
|
||||
* Email: phoenix@mail.ru
|
||||
* Sable Blitzmann
|
||||
* GitHub: @blitzmann
|
||||
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
|
||||
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @blitzmann
|
||||
* Email: sable.blitzmann@gmail.com
|
||||
|
||||
## CCP Copyright Notice
|
||||
EVE Online, the EVE logo, EVE and all associated logos and designs are the intellectual property of CCP hf. All artwork, screenshots, characters, vehicles, storylines, world facts or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of CCP hf. EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. CCP hf. has granted permission to pyfa to use EVE Online and all associated logos and designs for promotional and information purposes on its website but does not endorse, and is not in any way affiliated with, pyfa. CCP is in no way responsible for the content on or functioning of this program, nor can it be liable for any damage arising from the use of this program.
|
||||
|
||||
@@ -33,6 +33,7 @@ pyfaPath = None
|
||||
savePath = None
|
||||
saveDB = None
|
||||
gameDB = None
|
||||
imgsZIP = None
|
||||
logPath = None
|
||||
loggingLevel = None
|
||||
logging_setup = None
|
||||
@@ -96,6 +97,7 @@ def defPaths(customSavePath=None):
|
||||
global savePath
|
||||
global saveDB
|
||||
global gameDB
|
||||
global imgsZIP
|
||||
global saveInRoot
|
||||
global logPath
|
||||
global cipher
|
||||
@@ -155,6 +157,10 @@ def defPaths(customSavePath=None):
|
||||
if not gameDB:
|
||||
gameDB = os.path.join(pyfaPath, "eve.db")
|
||||
|
||||
imgsZIP = getattr(configforced, "imgsZIP", imgsZIP)
|
||||
if not imgsZIP:
|
||||
imgsZIP = os.path.join(pyfaPath, "imgs.zip")
|
||||
|
||||
if debug:
|
||||
logFile = "pyfa_debug.log"
|
||||
else:
|
||||
|
||||
33
eos/calc.py
Normal file
33
eos/calc.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2019 Ryan Holmes
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True):
|
||||
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
|
||||
if distance is None:
|
||||
return 1
|
||||
if srcFalloffRange > 0:
|
||||
# Most modules cannot be activated when at 3x falloff range, with few exceptions like guns
|
||||
if restrictedRange and distance > srcOptimalRange + 3 * srcFalloffRange:
|
||||
return 0
|
||||
return 0.5 ** ((max(0, distance - srcOptimalRange) / srcFalloffRange) ** 2)
|
||||
elif distance <= srcOptimalRange:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
@@ -25,7 +25,7 @@ from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
from eos.db import gamedata_meta
|
||||
from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table
|
||||
from eos.db.gamedata.effect import typeeffects_table
|
||||
from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, MetaType, Traits
|
||||
from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, Traits, MetaGroup
|
||||
|
||||
items_table = Table("invtypes", gamedata_meta,
|
||||
Column("typeID", Integer, primary_key=True),
|
||||
@@ -41,9 +41,11 @@ items_table = Table("invtypes", gamedata_meta,
|
||||
Column("iconID", Integer),
|
||||
Column("graphicID", Integer),
|
||||
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True),
|
||||
Column("metaLevel", Integer),
|
||||
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID"), index=True),
|
||||
Column("variationParentTypeID", Integer, ForeignKey("invtypes.typeID"), index=True),
|
||||
Column("replacements", String))
|
||||
|
||||
from .metaGroup import metatypes_table # noqa
|
||||
from .traits import traits_table # noqa
|
||||
|
||||
mapper(Item, items_table,
|
||||
@@ -51,9 +53,8 @@ mapper(Item, items_table,
|
||||
"group" : relation(Group, backref=backref("items", cascade="all,delete")),
|
||||
"_Item__attributes": relation(Attribute, cascade='all, delete, delete-orphan', collection_class=attribute_mapped_collection('name')),
|
||||
"effects": relation(Effect, secondary=typeeffects_table, collection_class=attribute_mapped_collection('name')),
|
||||
"metaGroup" : relation(MetaType,
|
||||
primaryjoin=metatypes_table.c.typeID == items_table.c.typeID,
|
||||
uselist=False),
|
||||
"metaGroup" : relation(MetaGroup, backref=backref("items", cascade="all,delete")),
|
||||
"varParent" : relation(Item, backref=backref("varChildren", cascade="all,delete"), remote_side=items_table.c.typeID),
|
||||
"ID" : synonym("typeID"),
|
||||
"name" : synonym("typeName"),
|
||||
"description" : deferred(items_table.c.description),
|
||||
@@ -64,7 +65,6 @@ mapper(Item, items_table,
|
||||
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
|
||||
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
|
||||
secondary=dynamicApplicable_table,
|
||||
backref="applicableItems")
|
||||
})
|
||||
backref="applicableItems")})
|
||||
|
||||
Item.category = association_proxy("group", "category")
|
||||
|
||||
@@ -17,35 +17,17 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import relation, mapper, synonym
|
||||
from sqlalchemy import Table, Column, Integer, String
|
||||
from sqlalchemy.orm import mapper, synonym
|
||||
|
||||
from eos.db import gamedata_meta
|
||||
from eos.db.gamedata.item import items_table
|
||||
from eos.gamedata import Item, MetaGroup, MetaType
|
||||
from eos.gamedata import MetaGroup
|
||||
|
||||
metagroups_table = Table("invmetagroups", gamedata_meta,
|
||||
Column("metaGroupID", Integer, primary_key=True),
|
||||
Column("metaGroupName", String))
|
||||
|
||||
metatypes_table = Table("invmetatypes", gamedata_meta,
|
||||
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),
|
||||
Column("parentTypeID", Integer, ForeignKey("invtypes.typeID")),
|
||||
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID")))
|
||||
|
||||
mapper(MetaGroup, metagroups_table,
|
||||
properties={
|
||||
"ID" : synonym("metaGroupID"),
|
||||
"name": synonym("metaGroupName")
|
||||
})
|
||||
|
||||
mapper(MetaType, metatypes_table,
|
||||
properties={
|
||||
"ID" : synonym("metaGroupID"),
|
||||
"parent": relation(Item, primaryjoin=metatypes_table.c.parentTypeID == items_table.c.typeID),
|
||||
"items" : relation(Item, primaryjoin=metatypes_table.c.typeID == items_table.c.typeID),
|
||||
"info" : relation(MetaGroup, lazy=False)
|
||||
})
|
||||
|
||||
MetaType.name = association_proxy("info", "name")
|
||||
"name": synonym("metaGroupName")})
|
||||
|
||||
@@ -23,8 +23,8 @@ from sqlalchemy.sql import and_, or_, select
|
||||
|
||||
import eos.config
|
||||
from eos.db import gamedata_session
|
||||
from eos.db.gamedata.item import items_table
|
||||
from eos.db.gamedata.group import groups_table
|
||||
from eos.db.gamedata.metaGroup import items_table, metatypes_table
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup
|
||||
|
||||
@@ -259,6 +259,10 @@ def getMetaGroup(lookfor, eager=None):
|
||||
return metaGroup
|
||||
|
||||
|
||||
def getMetaGroups():
|
||||
return gamedata_session.query(MetaGroup).all()
|
||||
|
||||
|
||||
@cachedQuery(1, "lookfor")
|
||||
def getMarketGroup(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
@@ -342,11 +346,9 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
|
||||
if len(itemids) == 0:
|
||||
return []
|
||||
|
||||
itemfilter = or_(*(metatypes_table.c.parentTypeID == itemid for itemid in itemids))
|
||||
itemfilter = or_(*(items_table.c.variationParentTypeID == itemid for itemid in itemids))
|
||||
filter = processWhere(itemfilter, where)
|
||||
joinon = items_table.c.typeID == metatypes_table.c.typeID
|
||||
vars = gamedata_session.query(Item).options(*processEager(eager)).join((metatypes_table, joinon)).filter(
|
||||
filter).all()
|
||||
vars = gamedata_session.query(Item).options(*processEager(eager)).filter(filter).all()
|
||||
|
||||
if vars:
|
||||
return vars
|
||||
|
||||
25
eos/db/migrations/upgrade34.py
Normal file
25
eos/db/migrations/upgrade34.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Migration 34
|
||||
|
||||
- Adds projection range columns to projectable entities
|
||||
"""
|
||||
import sqlalchemy
|
||||
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT projectionRange FROM projectedFits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE projectedFits ADD COLUMN projectionRange FLOAT;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT projectionRange FROM modules LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE modules ADD COLUMN projectionRange FLOAT;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT projectionRange FROM drones LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN projectionRange FLOAT;")
|
||||
try:
|
||||
saveddata_engine.execute("SELECT projectionRange FROM fighters LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fighters ADD COLUMN projectionRange FLOAT;")
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
import datetime
|
||||
|
||||
@@ -33,7 +33,8 @@ drones_table = Table("drones", saveddata_meta,
|
||||
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("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("projectionRange", Float, nullable=True)
|
||||
)
|
||||
|
||||
mapper(Drone, drones_table,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
import datetime
|
||||
|
||||
@@ -34,7 +34,8 @@ fighters_table = Table("fighters", saveddata_meta,
|
||||
Column("amount", 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("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("projectionRange", Float, nullable=True),
|
||||
)
|
||||
|
||||
fighter_abilities_table = Table("fightersAbilities", saveddata_meta,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Table
|
||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, Float, String, Table
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm import mapper, reconstructor, relation, relationship
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
@@ -70,7 +70,8 @@ projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||
Column("amount", Integer, nullable=False, default=1),
|
||||
Column("active", Boolean, nullable=False, default=1),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("projectionRange", Float, nullable=True),
|
||||
)
|
||||
|
||||
commandFits_table = Table("commandFits", saveddata_meta,
|
||||
@@ -83,6 +84,7 @@ commandFits_table = Table("commandFits", saveddata_meta,
|
||||
|
||||
|
||||
class ProjectedFit:
|
||||
|
||||
def __init__(self, sourceID, source_fit, amount=1, active=True):
|
||||
self.sourceID = sourceID
|
||||
self.source_fit = source_fit
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import eos.db
|
||||
from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern
|
||||
from eos.saveddata.targetProfile import TargetProfile as es_TargetProfile
|
||||
@@ -34,95 +35,97 @@ class DefaultDatabaseValues:
|
||||
|
||||
@classmethod
|
||||
def importDamageProfileDefaults(cls):
|
||||
damageProfileList = [["Uniform", "25", "25", "25", "25"], ["[Generic]EM", "100", "0", "0", "0"],
|
||||
["[Generic]Thermal", "0", "100", "0", "0"], ["[Generic]Kinetic", "0", "0", "100", "0"],
|
||||
["[Generic]Explosive", "0", "0", "0", "100"],
|
||||
["[NPC][Asteroid] Blood Raiders", "5067", "4214", "0", "0"],
|
||||
["[Bombs]Electron Bomb", "6400", "0", "0", "0"],
|
||||
["[Bombs]Scorch Bomb", "0", "6400", "0", "0"],
|
||||
["[Bombs]Concussion Bomb", "0", "0", "6400", "0"],
|
||||
["[Bombs]Shrapnel Bomb", "0", "0", "0", "6400"],
|
||||
["[Frequency Crystals][T2] Conflagration", "61.6", "61.6", "0", "0"],
|
||||
["[Frequency Crystals][T2] Scorch", "72", "16", "0", "0"],
|
||||
["[Frequency Crystals][T2] Gleam", "56", "56", "0", "0"],
|
||||
["[Frequency Crystals][T2] Aurora", "40", "24", "0", "0"],
|
||||
["[Frequency Crystals]Multifrequency", "61.6", "44", "0", "0"],
|
||||
["[Frequency Crystals]Gamma", "61.6", "35.2", "0", "0"],
|
||||
["[Frequency Crystals]Xray", "52.8", "35.2", "0", "0"],
|
||||
["[Frequency Crystals]Ultraviolet", "52.8", "26.4", "0", "0"],
|
||||
["[Frequency Crystals]Standard", "44", "26.4", "0", "0"],
|
||||
["[Frequency Crystals]Infrared", "44", "17.6", "0", "0"],
|
||||
["[Frequency Crystals]Microwave", "35.2", "17.6", "0", "0"],
|
||||
["[Frequency Crystals]Radio", "44", "0", "0", "0"],
|
||||
["[Hybrid Charges][T2] Void", "0", "61.6", "61.6", "0"],
|
||||
["[Hybrid Charges][T2] Null", "0", "48", "40", "0"],
|
||||
["[Hybrid Charges][T2] Javelin", "0", "64", "48", "0"],
|
||||
["[Hybrid Charges][T2] Spike", "0", "32", "32", "0"],
|
||||
["[Hybrid Charges]Antimatter", "0", "48", "67.2", "0"],
|
||||
["[Hybrid Charges]Plutonium", "0", "48", "57.6", "0"],
|
||||
["[Hybrid Charges]Uranium", "0", "38.4", "57.6", "0"],
|
||||
["[Hybrid Charges]Thorium", "0", "38.4", "48", "0"],
|
||||
["[Hybrid Charges]Lead", "0", "28.8", "48", "0"],
|
||||
["[Hybrid Charges]Iridium", "0", "28.8", "38.4", "0"],
|
||||
["[Hybrid Charges]Tungsten", "0", "19.2", "38.4", "0"],
|
||||
["[Hybrid Charges]Iron", "0", "19.2", "28.8", "0"],
|
||||
["[Missiles]Mjolnir", "100", "0", "0", "0"],
|
||||
["[Missiles]Inferno", "0", "100", "0", "0"],
|
||||
["[Missiles]Scourge", "0", "0", "100", "0"],
|
||||
["[Missiles]Nova", "0", "0", "0", "100"],
|
||||
["[Missiles][Structure] Standup Missile", "100", "100", "100", "100"],
|
||||
["[Projectile Ammo][T2] Hail", "0", "0", "26.4", "96.8"],
|
||||
["[Projectile Ammo][T2] Barrage", "0", "0", "40", "48"],
|
||||
["[Projectile Ammo][T2] Quake", "0", "0", "40", "72"],
|
||||
["[Projectile Ammo][T2] Tremor", "0", "0", "24", "40"],
|
||||
["[Projectile Ammo]EMP", "79.2", "0", "8.8", "17.6"],
|
||||
["[Projectile Ammo]Phased Plasma", "0", "88", "17.6", "0"],
|
||||
["[Projectile Ammo]Fusion", "0", "0", "17.6", "88"],
|
||||
["[Projectile Ammo]Depleted Uranium", "0", "26.4", "17.6", "26.4"],
|
||||
["[Projectile Ammo]Titanium Sabot", "0", "0", "52.8", "176"],
|
||||
["[Projectile Ammo]Proton", "26.4", "0", "17.6", "0"],
|
||||
["[Projectile Ammo]Carbonized Lead", "0", "0", "35.2", "8.8"],
|
||||
["[Projectile Ammo]Nuclear", "0", "0", "8.8", "35.2"],
|
||||
damageProfileList = [["Uniform", 25, 25, 25, 25],
|
||||
["[Generic]EM", 1, 0, 0, 0],
|
||||
["[Generic]Thermal", 0, 1, 0, 0],
|
||||
["[Generic]Kinetic", 0, 0, 1, 0],
|
||||
["[Generic]Explosive", 0, 0, 0, 1],
|
||||
["[NPC][Asteroid] Blood Raiders", 5067, 4214, 0, 0],
|
||||
["[Bombs]Electron Bomb", 6400, 0, 0, 0],
|
||||
["[Bombs]Scorch Bomb", 0, 6400, 0, 0],
|
||||
["[Bombs]Concussion Bomb", 0, 0, 6400, 0],
|
||||
["[Bombs]Shrapnel Bomb", 0, 0, 0, 6400],
|
||||
["[Frequency Crystals][T2] Conflagration", 7.7, 7.7, 0, 0],
|
||||
["[Frequency Crystals][T2] Scorch", 9, 2, 0, 0],
|
||||
["[Frequency Crystals][T2] Gleam", 7, 7, 0, 0],
|
||||
["[Frequency Crystals][T2] Aurora", 5, 3, 0, 0],
|
||||
["[Frequency Crystals]Multifrequency", 7, 5, 0, 0],
|
||||
["[Frequency Crystals]Gamma", 7, 4, 0, 0],
|
||||
["[Frequency Crystals]Xray", 6, 4, 0, 0],
|
||||
["[Frequency Crystals]Ultraviolet", 6, 3, 0, 0],
|
||||
["[Frequency Crystals]Standard", 5, 3, 0, 0],
|
||||
["[Frequency Crystals]Infrared", 5, 2, 0, 0],
|
||||
["[Frequency Crystals]Microwave", 4, 2, 0, 0],
|
||||
["[Frequency Crystals]Radio", 5, 0, 0, 0],
|
||||
["[Hybrid Charges][T2] Void", 0, 7.7, 7.7, 0],
|
||||
["[Hybrid Charges][T2] Null", 0, 6, 5, 0],
|
||||
["[Hybrid Charges][T2] Javelin", 0, 8, 6, 0],
|
||||
["[Hybrid Charges][T2] Spike", 0, 4, 4, 0],
|
||||
["[Hybrid Charges]Antimatter", 0, 5, 7, 0],
|
||||
["[Hybrid Charges]Plutonium", 0, 5, 6, 0],
|
||||
["[Hybrid Charges]Uranium", 0, 4, 6, 0],
|
||||
["[Hybrid Charges]Thorium", 0, 4, 5, 0],
|
||||
["[Hybrid Charges]Lead", 0, 3, 5, 0],
|
||||
["[Hybrid Charges]Iridium", 0, 3, 4, 0],
|
||||
["[Hybrid Charges]Tungsten", 0, 2, 4, 0],
|
||||
["[Hybrid Charges]Iron", 0, 2, 3, 0],
|
||||
["[Missiles]Mjolnir", 1, 0, 0, 0],
|
||||
["[Missiles]Inferno", 0, 1, 0, 0],
|
||||
["[Missiles]Scourge", 0, 0, 1, 0],
|
||||
["[Missiles]Nova", 0, 0, 0, 1],
|
||||
["[Missiles][Structure] Standup Missile", 1, 1, 1, 1],
|
||||
["[Projectile Ammo][T2] Hail", 0, 0, 3.3, 12.1],
|
||||
["[Projectile Ammo][T2] Barrage", 0, 0, 5, 6],
|
||||
["[Projectile Ammo][T2] Quake", 0, 0, 5, 9],
|
||||
["[Projectile Ammo][T2] Tremor", 0, 0, 3, 5],
|
||||
["[Projectile Ammo]EMP", 9, 0, 1, 2],
|
||||
["[Projectile Ammo]Phased Plasma", 0, 10, 2, 0],
|
||||
["[Projectile Ammo]Fusion", 0, 0, 2, 10],
|
||||
["[Projectile Ammo]Depleted Uranium", 0, 3, 2, 3],
|
||||
["[Projectile Ammo]Titanium Sabot", 0, 0, 6, 2],
|
||||
["[Projectile Ammo]Proton", 3, 0, 2, 0],
|
||||
["[Projectile Ammo]Carbonized Lead", 0, 0, 4, 1],
|
||||
["[Projectile Ammo]Nuclear", 0, 0, 1, 4],
|
||||
# Different sizes of plasma do different damage, the values here are
|
||||
# average of proportions across sizes
|
||||
["[Exotic Plasma][T2] Occult", "0", "55863", "0", "44137"],
|
||||
["[Exotic Plasma][T2] Mystic", "0", "66319", "0", "33681"],
|
||||
["[Exotic Plasma]Tetryon", "0", "69208", "0", "30792"],
|
||||
["[Exotic Plasma]Baryon", "0", "59737", "0", "40263"],
|
||||
["[Exotic Plasma]Meson", "0", "60519", "0", "39481"],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", "90", "90", "0", "0"],
|
||||
["[NPC][Burner] Dramiel (Angel)", "55", "0", "20", "96"],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", "0", "110", "154", "0"],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", "135", "30", "0", "0"],
|
||||
["[NPC][Burner] Worm (Guristas)", "0", "0", "228", "0"],
|
||||
["[NPC][Burner] Enyo", "0", "147", "147", "0"],
|
||||
["[NPC][Burner] Hawk", "0", "0", "247", "0"],
|
||||
["[NPC][Burner] Jaguar", "36", "0", "50", "182"],
|
||||
["[NPC][Burner] Vengeance", "232", "0", "0", "0"],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", "260", "100", "0", "0"],
|
||||
["[NPC][Burner] Talos", "0", "413", "413", "0"],
|
||||
["[NPC][Burner] Sentinel", "0", "75", "0", "90"],
|
||||
["[NPC][Asteroid] Angel Cartel", "1838", "562", "2215", "3838"],
|
||||
["[NPC][Deadspace] Angel Cartel", "369", "533", "1395", "3302"],
|
||||
["[NPC][Deadspace] Blood Raiders", "6040", "5052", "10", "15"],
|
||||
["[NPC][Asteroid] Guristas", "0", "1828", "7413", "0"],
|
||||
["[NPC][Deadspace] Guristas", "0", "1531", "9680", "0"],
|
||||
["[NPC][Asteroid] Rogue Drone", "394", "666", "1090", "1687"],
|
||||
["[NPC][Deadspace] Rogue Drone", "276", "1071", "1069", "871"],
|
||||
["[NPC][Asteroid] Sanshas Nation", "5586", "4112", "0", "0"],
|
||||
["[NPC][Deadspace] Sanshas Nation", "3009", "2237", "0", "0"],
|
||||
["[NPC][Asteroid] Serpentis", "0", "5373", "4813", "0"],
|
||||
["[NPC][Deadspace] Serpentis", "0", "3110", "1929", "0"],
|
||||
["[NPC][Mission] Amarr Empire", "4464", "3546", "97", "0"],
|
||||
["[NPC][Mission] Caldari State", "0", "2139", "4867", "0"],
|
||||
["[NPC][Mission] CONCORD", "336", "134", "212", "412"],
|
||||
["[NPC][Mission] Gallente Federation", "9", "3712", "2758", "0"],
|
||||
["[NPC][Mission] Khanid", "612", "483", "43", "6"],
|
||||
["[NPC][Mission] Minmatar Republic", "1024", "388", "1655", "4285"],
|
||||
["[NPC][Mission] Mordus Legion", "25", "262", "625", "0"],
|
||||
["[NPC][Mission] Thukker", "0", "52", "10", "79"],
|
||||
["[NPC][Other] Sleepers", "1472", "1472", "1384", "1384"],
|
||||
["[NPC][Other] Sansha Incursion", "1682", "1347", "3678", "3678"]]
|
||||
["[Exotic Plasma][T2] Occult", 0, 55863, 0, 44137],
|
||||
["[Exotic Plasma][T2] Mystic", 0, 66319, 0, 33681],
|
||||
["[Exotic Plasma]Tetryon", 0, 69208, 0, 30792],
|
||||
["[Exotic Plasma]Baryon", 0, 59737, 0, 40263],
|
||||
["[Exotic Plasma]Meson", 0, 60519, 0, 39481],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", 90, 90, 0, 0],
|
||||
["[NPC][Burner] Dramiel (Angel)", 55, 0, 20, 96],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", 0, 110, 154, 0],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", 135, 30, 0, 0],
|
||||
["[NPC][Burner] Worm (Guristas)", 0, 0, 228, 0],
|
||||
["[NPC][Burner] Enyo", 0, 147, 147, 0],
|
||||
["[NPC][Burner] Hawk", 0, 0, 247, 0],
|
||||
["[NPC][Burner] Jaguar", 36, 0, 50, 182],
|
||||
["[NPC][Burner] Vengeance", 232, 0, 0, 0],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", 260, 100, 0, 0],
|
||||
["[NPC][Burner] Talos", 0, 413, 413, 0],
|
||||
["[NPC][Burner] Sentinel", 0, 75, 0, 90],
|
||||
["[NPC][Asteroid] Angel Cartel", 1838, 562, 2215, 3838],
|
||||
["[NPC][Deadspace] Angel Cartel", 369, 533, 1395, 3302],
|
||||
["[NPC][Deadspace] Blood Raiders", 6040, 5052, 10, 15],
|
||||
["[NPC][Asteroid] Guristas", 0, 1828, 7413, 0],
|
||||
["[NPC][Deadspace] Guristas", 0, 1531, 9680, 0],
|
||||
["[NPC][Asteroid] Rogue Drone", 394, 666, 1090, 1687],
|
||||
["[NPC][Deadspace] Rogue Drone", 276, 1071, 1069, 871],
|
||||
["[NPC][Asteroid] Sanshas Nation", 5586, 4112, 0, 0],
|
||||
["[NPC][Deadspace] Sanshas Nation", 3009, 2237, 0, 0],
|
||||
["[NPC][Asteroid] Serpentis", 0, 5373, 4813, 0],
|
||||
["[NPC][Deadspace] Serpentis", 0, 3110, 1929, 0],
|
||||
["[NPC][Mission] Amarr Empire", 4464, 3546, 97, 0],
|
||||
["[NPC][Mission] Caldari State", 0, 2139, 4867, 0],
|
||||
["[NPC][Mission] CONCORD", 336, 134, 212, 412],
|
||||
["[NPC][Mission] Gallente Federation", 9, 3712, 2758, 0],
|
||||
["[NPC][Mission] Khanid", 612, 483, 43, 6],
|
||||
["[NPC][Mission] Minmatar Republic", 1024, 388, 1655, 4285],
|
||||
["[NPC][Mission] Mordus Legion", 25, 262, 625, 0],
|
||||
["[NPC][Mission] Thukker", 0, 52, 10, 79],
|
||||
["[NPC][Other] Sleepers", 1472, 1472, 1384, 1384],
|
||||
["[NPC][Other] Sansha Incursion", 1682, 1347, 3678, 3678]]
|
||||
|
||||
for damageProfileRow in damageProfileList:
|
||||
name, em, therm, kin, exp = damageProfileRow
|
||||
@@ -130,61 +133,67 @@ class DefaultDatabaseValues:
|
||||
if damageProfile is None:
|
||||
damageProfile = es_DamagePattern(em, therm, kin, exp)
|
||||
damageProfile.name = name
|
||||
eos.db.save(damageProfile)
|
||||
eos.db.add(damageProfile)
|
||||
else:
|
||||
damageProfile.emAmount = em
|
||||
damageProfile.thermalAmount = therm
|
||||
damageProfile.kineticAmount = kin
|
||||
damageProfile.explosiveAmount = exp
|
||||
eos.db.commit()
|
||||
|
||||
@classmethod
|
||||
def importTargetProfileDefaults(cls):
|
||||
targetProfileList = [["Uniform (25%)", "0.25", "0.25", "0.25", "0.25"],
|
||||
["Uniform (50%)", "0.50", "0.50", "0.50", "0.50"],
|
||||
["Uniform (75%)", "0.75", "0.75", "0.75", "0.75"],
|
||||
["Uniform (90%)", "0.90", "0.90", "0.90", "0.90"],
|
||||
["[T1 Resist]Shield", "0.0", "0.20", "0.40", "0.50"],
|
||||
["[T1 Resist]Armor", "0.50", "0.45", "0.25", "0.10"],
|
||||
["[T1 Resist]Hull", "0.33", "0.33", "0.33", "0.33"],
|
||||
["[T1 Resist]Shield (+T2 DCU)", "0.125", "0.30", "0.475", "0.562"],
|
||||
["[T1 Resist]Armor (+T2 DCU)", "0.575", "0.532", "0.363", "0.235"],
|
||||
["[T1 Resist]Hull (+T2 DCU)", "0.598", "0.598", "0.598", "0.598"],
|
||||
["[T2 Resist]Amarr (Shield)", "0.0", "0.20", "0.70", "0.875"],
|
||||
["[T2 Resist]Amarr (Armor)", "0.50", "0.35", "0.625", "0.80"],
|
||||
["[T2 Resist]Caldari (Shield)", "0.20", "0.84", "0.76", "0.60"],
|
||||
["[T2 Resist]Caldari (Armor)", "0.50", "0.8625", "0.625", "0.10"],
|
||||
["[T2 Resist]Gallente (Shield)", "0.0", "0.60", "0.85", "0.50"],
|
||||
["[T2 Resist]Gallente (Armor)", "0.50", "0.675", "0.8375", "0.10"],
|
||||
["[T2 Resist]Minmatar (Shield)", "0.75", "0.60", "0.40", "0.50"],
|
||||
["[T2 Resist]Minmatar (Armor)", "0.90", "0.675", "0.25", "0.10"],
|
||||
["[NPC][Asteroid] Angel Cartel", "0.54", "0.42", "0.37", "0.32"],
|
||||
["[NPC][Asteroid] Blood Raiders", "0.34", "0.39", "0.45", "0.52"],
|
||||
["[NPC][Asteroid] Guristas", "0.55", "0.35", "0.3", "0.48"],
|
||||
["[NPC][Asteroid] Rogue Drones", "0.35", "0.38", "0.44", "0.49"],
|
||||
["[NPC][Asteroid] Sanshas Nation", "0.35", "0.4", "0.47", "0.53"],
|
||||
["[NPC][Asteroid] Serpentis", "0.49", "0.38", "0.29", "0.51"],
|
||||
["[NPC][Deadspace] Angel Cartel", "0.59", "0.48", "0.4", "0.32"],
|
||||
["[NPC][Deadspace] Blood Raiders", "0.31", "0.39", "0.47", "0.56"],
|
||||
["[NPC][Deadspace] Guristas", "0.57", "0.39", "0.31", "0.5"],
|
||||
["[NPC][Deadspace] Rogue Drones", "0.42", "0.42", "0.47", "0.49"],
|
||||
["[NPC][Deadspace] Sanshas Nation", "0.31", "0.39", "0.47", "0.56"],
|
||||
["[NPC][Deadspace] Serpentis", "0.49", "0.38", "0.29", "0.56"],
|
||||
["[NPC][Mission] Amarr Empire", "0.34", "0.38", "0.42", "0.46"],
|
||||
["[NPC][Mission] Caldari State", "0.51", "0.38", "0.3", "0.51"],
|
||||
["[NPC][Mission] CONCORD", "0.47", "0.46", "0.47", "0.47"],
|
||||
["[NPC][Mission] Gallente Federation", "0.51", "0.38", "0.31", "0.52"],
|
||||
["[NPC][Mission] Khanid", "0.51", "0.42", "0.36", "0.4"],
|
||||
["[NPC][Mission] Minmatar Republic", "0.51", "0.46", "0.41", "0.35"],
|
||||
["[NPC][Mission] Mordus Legion", "0.32", "0.48", "0.4", "0.62"],
|
||||
["[NPC][Other] Sleeper", "0.61", "0.61", "0.61", "0.61"],
|
||||
["[NPC][Other] Sansha Incursion", "0.65", "0.63", "0.64", "0.65"],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", "0.8", "0.73", "0.69", "0.67"],
|
||||
["[NPC][Burner] Dramiel (Angel)", "0.35", "0.48", "0.61", "0.68"],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", "0.69", "0.59", "0.59", "0.43"],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", "0.35", "0.48", "0.61", "0.68"],
|
||||
["[NPC][Burner] Worm (Guristas)", "0.48", "0.58", "0.69", "0.74"],
|
||||
["[NPC][Burner] Enyo", "0.58", "0.72", "0.86", "0.24"],
|
||||
["[NPC][Burner] Hawk", "0.3", "0.86", "0.79", "0.65"],
|
||||
["[NPC][Burner] Jaguar", "0.78", "0.65", "0.48", "0.56"],
|
||||
["[NPC][Burner] Vengeance", "0.66", "0.56", "0.75", "0.86"],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", "0.8", "0.76", "0.68", "0.7"],
|
||||
["[NPC][Burner] Talos", "0.68", "0.59", "0.59", "0.43"],
|
||||
["[NPC][Burner] Sentinel", "0.58", "0.45", "0.52", "0.66"]]
|
||||
targetProfileList = [["Uniform (25%)", 0.25, 0.25, 0.25, 0.25],
|
||||
["Uniform (50%)", 0.50, 0.50, 0.50, 0.50],
|
||||
["Uniform (75%)", 0.75, 0.75, 0.75, 0.75],
|
||||
["Uniform (90%)", 0.90, 0.90, 0.90, 0.90],
|
||||
["[T1 Resist]Shield", 0.0, 0.20, 0.40, 0.50],
|
||||
["[T1 Resist]Armor", 0.50, 0.45, 0.25, 0.10],
|
||||
["[T1 Resist]Hull", 0.33, 0.33, 0.33, 0.33],
|
||||
["[T1 Resist]Shield (+T2 DCU)", 0.125, 0.30, 0.475, 0.562],
|
||||
["[T1 Resist]Armor (+T2 DCU)", 0.575, 0.532, 0.363, 0.235],
|
||||
["[T1 Resist]Hull (+T2 DCU)", 0.598, 0.598, 0.598, 0.598],
|
||||
["[T2 Resist]Amarr (Shield)", 0.0, 0.20, 0.70, 0.875],
|
||||
["[T2 Resist]Amarr (Armor)", 0.50, 0.35, 0.625, 0.80],
|
||||
["[T2 Resist]Caldari (Shield)", 0.20, 0.84, 0.76, 0.60],
|
||||
["[T2 Resist]Caldari (Armor)", 0.50, 0.8625, 0.625, 0.10],
|
||||
["[T2 Resist]Gallente (Shield)", 0.0, 0.60, 0.85, 0.50],
|
||||
["[T2 Resist]Gallente (Armor)", 0.50, 0.675, 0.8375, 0.10],
|
||||
["[T2 Resist]Minmatar (Shield)", 0.75, 0.60, 0.40, 0.50],
|
||||
["[T2 Resist]Minmatar (Armor)", 0.90, 0.675, 0.25, 0.10],
|
||||
["[NPC][Asteroid] Angel Cartel", 0.54, 0.42, 0.37, 0.32],
|
||||
["[NPC][Asteroid] Blood Raiders", 0.34, 0.39, 0.45, 0.52],
|
||||
["[NPC][Asteroid] Guristas", 0.55, 0.35, 0.3, 0.48],
|
||||
["[NPC][Asteroid] Rogue Drones", 0.35, 0.38, 0.44, 0.49],
|
||||
["[NPC][Asteroid] Sanshas Nation", 0.35, 0.4, 0.47, 0.53],
|
||||
["[NPC][Asteroid] Serpentis", 0.49, 0.38, 0.29, 0.51],
|
||||
["[NPC][Deadspace] Angel Cartel", 0.59, 0.48, 0.4, 0.32],
|
||||
["[NPC][Deadspace] Blood Raiders", 0.31, 0.39, 0.47, 0.56],
|
||||
["[NPC][Deadspace] Guristas", 0.57, 0.39, 0.31, 0.5],
|
||||
["[NPC][Deadspace] Rogue Drones", 0.42, 0.42, 0.47, 0.49],
|
||||
["[NPC][Deadspace] Sanshas Nation", 0.31, 0.39, 0.47, 0.56],
|
||||
["[NPC][Deadspace] Serpentis", 0.49, 0.38, 0.29, 0.56],
|
||||
["[NPC][Mission] Amarr Empire", 0.34, 0.38, 0.42, 0.46],
|
||||
["[NPC][Mission] Caldari State", 0.51, 0.38, 0.3, 0.51],
|
||||
["[NPC][Mission] CONCORD", 0.47, 0.46, 0.47, 0.47],
|
||||
["[NPC][Mission] Gallente Federation", 0.51, 0.38, 0.31, 0.52],
|
||||
["[NPC][Mission] Khanid", 0.51, 0.42, 0.36, 0.4],
|
||||
["[NPC][Mission] Minmatar Republic", 0.51, 0.46, 0.41, 0.35],
|
||||
["[NPC][Mission] Mordus Legion", 0.32, 0.48, 0.4, 0.62],
|
||||
["[NPC][Other] Sleeper", 0.61, 0.61, 0.61, 0.61],
|
||||
["[NPC][Other] Sansha Incursion", 0.65, 0.63, 0.64, 0.65],
|
||||
["[NPC][Burner] Cruor (Blood Raiders)", 0.8, 0.73, 0.69, 0.67],
|
||||
["[NPC][Burner] Dramiel (Angel)", 0.35, 0.48, 0.61, 0.68],
|
||||
["[NPC][Burner] Daredevil (Serpentis)", 0.69, 0.59, 0.59, 0.43],
|
||||
["[NPC][Burner] Succubus (Sanshas Nation)", 0.35, 0.48, 0.61, 0.68],
|
||||
["[NPC][Burner] Worm (Guristas)", 0.48, 0.58, 0.69, 0.74],
|
||||
["[NPC][Burner] Enyo", 0.58, 0.72, 0.86, 0.24],
|
||||
["[NPC][Burner] Hawk", 0.3, 0.86, 0.79, 0.65],
|
||||
["[NPC][Burner] Jaguar", 0.78, 0.65, 0.48, 0.56],
|
||||
["[NPC][Burner] Vengeance", 0.66, 0.56, 0.75, 0.86],
|
||||
["[NPC][Burner] Ashimmu (Blood Raiders)", 0.8, 0.76, 0.68, 0.7],
|
||||
["[NPC][Burner] Talos", 0.68, 0.59, 0.59, 0.43],
|
||||
["[NPC][Burner] Sentinel", 0.58, 0.45, 0.52, 0.66]]
|
||||
|
||||
for targetProfileRow in targetProfileList:
|
||||
name = targetProfileRow[0]
|
||||
@@ -204,15 +213,25 @@ class DefaultDatabaseValues:
|
||||
radius = targetProfileRow[7]
|
||||
except:
|
||||
radius = None
|
||||
targetProfile = eos.db.eos.db.getTargetProfile(name)
|
||||
targetProfile = eos.db.getTargetProfile(name)
|
||||
if targetProfile is None:
|
||||
targetProfile = es_TargetProfile(em, therm, kin, exp, maxVel, sigRad, radius)
|
||||
targetProfile.name = name
|
||||
eos.db.save(targetProfile)
|
||||
eos.db.add(targetProfile)
|
||||
else:
|
||||
targetProfile.emAmount = em
|
||||
targetProfile.thermalAmount = therm
|
||||
targetProfile.kineticAmount = kin
|
||||
targetProfile.explosiveAmount = exp
|
||||
targetProfile.maxVelocity = maxVel
|
||||
targetProfile.signatureRadius = sigRad
|
||||
targetProfile.radius = radius
|
||||
eos.db.commit()
|
||||
|
||||
|
||||
@classmethod
|
||||
def importRequiredDefaults(cls):
|
||||
damageProfileList = [["Uniform", "25", "25", "25", "25"]]
|
||||
damageProfileList = [["Uniform", 25, 25, 25, 25]]
|
||||
|
||||
for damageProfileRow in damageProfileList:
|
||||
name, em, therm, kin, exp = damageProfileRow
|
||||
@@ -220,4 +239,10 @@ class DefaultDatabaseValues:
|
||||
if damageProfile is None:
|
||||
damageProfile = es_DamagePattern(em, therm, kin, exp)
|
||||
damageProfile.name = name
|
||||
eos.db.save(damageProfile)
|
||||
eos.db.add(damageProfile)
|
||||
else:
|
||||
damageProfile.emAmount = em
|
||||
damageProfile.thermalAmount = therm
|
||||
damageProfile.kineticAmount = kin
|
||||
damageProfile.explosiveAmount = exp
|
||||
eos.db.commit()
|
||||
|
||||
@@ -42,6 +42,7 @@ modules_table = Table("modules", saveddata_meta,
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
Column("spoolType", Integer, nullable=True),
|
||||
Column("spoolAmount", Float, nullable=True),
|
||||
Column("projectionRange", Float, nullable=True),
|
||||
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
|
||||
|
||||
mapper(Module, modules_table,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm.collections import collection
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -138,9 +139,10 @@ class HandledModuleList(HandledList):
|
||||
else:
|
||||
self.appendIgnoreEmpty(mod)
|
||||
|
||||
@collection.appender
|
||||
def appendIgnoreEmpty(self, mod):
|
||||
mod.position = len(self)
|
||||
HandledList.append(self, mod)
|
||||
super().append(mod)
|
||||
if mod.isInvalid:
|
||||
self.remove(mod)
|
||||
|
||||
|
||||
4909
eos/effects.py
4909
eos/effects.py
File diff suppressed because it is too large
Load Diff
@@ -530,6 +530,14 @@ class Item(EqBase):
|
||||
def isBooster(self):
|
||||
return self.group.name == 'Booster' and self.category.name == 'Implant'
|
||||
|
||||
@property
|
||||
def isStandup(self):
|
||||
if self.category.name == "Structure Module":
|
||||
return True
|
||||
if self.isFighter and {'fighterSquadronIsStandupLight', 'fighterSquadronIsStandupHeavy', 'fighterSquadronIsStandupSupport'}.intersection(self.attributes):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "Item(ID={}, name={}) at {}".format(
|
||||
self.ID, self.name, hex(id(self))
|
||||
@@ -601,10 +609,6 @@ class MetaGroup(EqBase):
|
||||
pass
|
||||
|
||||
|
||||
class MetaType(EqBase):
|
||||
pass
|
||||
|
||||
|
||||
class Unit(EqBase):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -122,7 +122,7 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
(effect.isType("passive") or effect.isType("boosterSideEffect")):
|
||||
if effect.isType("boosterSideEffect") and effect not in self.activeSideEffectEffects:
|
||||
continue
|
||||
effect.handler(fit, self, ("booster",))
|
||||
effect.handler(fit, self, ("booster",), None, effect=effect)
|
||||
|
||||
@validates("ID", "itemID", "ammoID", "active")
|
||||
def validator(self, key, val):
|
||||
|
||||
@@ -422,7 +422,7 @@ class Skill(HandledItem):
|
||||
(not fit.isStructure or effect.isType("structure")) and \
|
||||
effect.activeByDefault:
|
||||
try:
|
||||
effect.handler(fit, self, ("skill",))
|
||||
effect.handler(fit, self, ("skill",), None, effect=effect)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
@@ -25,6 +26,7 @@ import eos.db
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.utils.cycles import CycleInfo
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
|
||||
|
||||
@@ -45,6 +47,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.amount = 0
|
||||
self.amountActive = 0
|
||||
self.projected = False
|
||||
self.projectionRange = None
|
||||
self.build()
|
||||
|
||||
@reconstructor
|
||||
@@ -304,7 +307,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
else:
|
||||
return True
|
||||
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False):
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, forcedProjRange=DEFAULT):
|
||||
if self.projected or forceProjected:
|
||||
context = "projected", "drone"
|
||||
projected = True
|
||||
@@ -312,6 +315,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
context = ("drone",)
|
||||
projected = False
|
||||
|
||||
projectionRange = self.projectionRange if forcedProjRange is DEFAULT else forcedProjRange
|
||||
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
effect.activeByDefault and \
|
||||
@@ -319,36 +324,34 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
projected is False and effect.isType("passive")):
|
||||
# See GH issue #765
|
||||
if effect.getattr('grouped'):
|
||||
try:
|
||||
effect.handler(fit, self, context, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
else:
|
||||
i = 0
|
||||
while i != self.amountActive:
|
||||
try:
|
||||
effect.handler(fit, self, context, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
i += 1
|
||||
|
||||
if self.charge:
|
||||
for effect in self.charge.effects.values():
|
||||
if effect.runTime == runTime and effect.activeByDefault:
|
||||
effect.handler(fit, self, ("droneCharge",))
|
||||
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Drone(self.item)
|
||||
copy.amount = self.amount
|
||||
copy.amountActive = self.amountActive
|
||||
copy.projectionRange = self.projectionRange
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
amount = self.amount
|
||||
amountActive = self.amountActive
|
||||
projectionRange = self.projectionRange
|
||||
|
||||
Drone.__init__(self, item)
|
||||
self.amount = amount
|
||||
self.amountActive = amountActive
|
||||
self.projectionRange = projectionRange
|
||||
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
@@ -27,8 +28,9 @@ from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.stats import DmgTypes
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -47,6 +49,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
self.itemID = item.ID if item is not None else None
|
||||
self.projected = False
|
||||
self.projectionRange = None
|
||||
self.active = True
|
||||
|
||||
# -1 is a placeholder that represents max squadron size, which we may not know yet as ships may modify this with
|
||||
@@ -380,7 +383,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
else:
|
||||
return True
|
||||
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False):
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, forcedProjRange=DEFAULT):
|
||||
if not self.active:
|
||||
return
|
||||
|
||||
@@ -391,6 +394,8 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
context = ("fighter",)
|
||||
projected = False
|
||||
|
||||
projectionRange = self.projectionRange if forcedProjRange is DEFAULT else forcedProjRange
|
||||
|
||||
for ability in self.abilities:
|
||||
if not ability.active:
|
||||
continue
|
||||
@@ -399,17 +404,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if effect.runTime == runTime and effect.activeByDefault and \
|
||||
((projected and effect.isType("projected")) or not projected):
|
||||
if ability.grouped:
|
||||
try:
|
||||
effect.handler(fit, self, context, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
else:
|
||||
i = 0
|
||||
while i != self.amount:
|
||||
try:
|
||||
effect.handler(fit, self, context, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
i += 1
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
@@ -419,18 +418,22 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
for ability in self.abilities:
|
||||
copyAbility = next(filter(lambda a: a.effectID == ability.effectID, copy.abilities))
|
||||
copyAbility.active = ability.active
|
||||
copy.projectionRange = self.projectionRange
|
||||
return copy
|
||||
|
||||
def rebase(self, item):
|
||||
amount = self._amount
|
||||
active = self.active
|
||||
abilityEffectStates = {a.effectID: a.active for a in self.abilities}
|
||||
projectionRange = self.projectionRange
|
||||
|
||||
Fighter.__init__(self, item)
|
||||
self._amount = amount
|
||||
self.active = active
|
||||
for ability in self.abilities:
|
||||
if ability.effectID in abilityEffectStates:
|
||||
ability.active = abilityEffectStates[ability.effectID]
|
||||
self.projectionRange = projectionRange
|
||||
|
||||
def fits(self, fit):
|
||||
# If ships doesn't support this type of fighter, don't add it
|
||||
|
||||
@@ -21,9 +21,9 @@ import datetime
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from math import asinh, log, sqrt
|
||||
|
||||
from logbook import Logger
|
||||
from math import asinh, log, sqrt
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
@@ -440,10 +440,8 @@ class Fit:
|
||||
return False
|
||||
|
||||
# Citadel modules are now under a new category, so we can check this to ensure only structure modules can fit on a citadel
|
||||
if isinstance(self.ship, Citadel) and item.category.name != "Structure Module" or \
|
||||
not isinstance(self.ship, Citadel) and item.category.name == "Structure Module":
|
||||
if isinstance(self.ship, Citadel) is not item.isStandup:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clear(self, projected=False, command=False):
|
||||
@@ -934,13 +932,20 @@ class Fit:
|
||||
To support a simpler way of doing self projections (so that we don't have to make a copy of the fit and
|
||||
recalculate), this function was developed to be a common source of projected effect application.
|
||||
"""
|
||||
c = chain(self.drones, self.fighters, self.modules)
|
||||
for item in c:
|
||||
for item in chain(self.drones, self.fighters):
|
||||
if item is not None:
|
||||
# apply effects onto target fit x amount of times
|
||||
for _ in range(projectionInfo.amount):
|
||||
targetFit.register(item, origin=self)
|
||||
item.calculateModifiedAttributes(targetFit, runTime, True)
|
||||
item.calculateModifiedAttributes(
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=0)
|
||||
for mod in self.modules:
|
||||
for _ in range(projectionInfo.amount):
|
||||
targetFit.register(mod, origin=self)
|
||||
mod.calculateModifiedAttributes(
|
||||
targetFit, runTime, forceProjected=True,
|
||||
forcedProjRange=projectionInfo.projectionRange)
|
||||
|
||||
def fill(self):
|
||||
"""
|
||||
@@ -1219,8 +1224,8 @@ class Fit:
|
||||
# Signature reduction, uses the bomb formula as per CCP Larrikin
|
||||
if energyNeutralizerSignatureResolution:
|
||||
capNeed = capNeed * min(1, signatureRadius / energyNeutralizerSignatureResolution)
|
||||
|
||||
self.__extraDrains.append((cycleTime, capNeed, clipSize, reloadTime))
|
||||
if capNeed:
|
||||
self.__extraDrains.append((cycleTime, capNeed, clipSize, reloadTime))
|
||||
|
||||
def removeDrain(self, i):
|
||||
del self.__extraDrains[i]
|
||||
@@ -1671,6 +1676,8 @@ class Fit:
|
||||
copyProjectionInfo = fit.getProjectionInfo(fitCopy.ID)
|
||||
originalProjectionInfo = fit.getProjectionInfo(self.ID)
|
||||
copyProjectionInfo.active = originalProjectionInfo.active
|
||||
copyProjectionInfo.amount = originalProjectionInfo.amount
|
||||
copyProjectionInfo.projectionRange = originalProjectionInfo.projectionRange
|
||||
forceUpdateSavedata(fit)
|
||||
|
||||
return fitCopy
|
||||
|
||||
@@ -95,7 +95,7 @@ class Implant(HandledItem, ItemAttrShortcut):
|
||||
return
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and effect.isType("passive") and effect.activeByDefault:
|
||||
effect.handler(fit, self, ("implant",))
|
||||
effect.handler(fit, self, ("implant",), None, effect=effect)
|
||||
|
||||
@validates("fitID", "itemID", "active")
|
||||
def validator(self, key, val):
|
||||
|
||||
@@ -54,7 +54,7 @@ class Mode(ItemAttrShortcut, HandledItem):
|
||||
if self.item:
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and effect.activeByDefault:
|
||||
effect.handler(fit, self, context=("module",))
|
||||
effect.handler(fit, self, ("module",), None, effect=effect)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Mode(self.item)
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from logbook import Logger
|
||||
import math
|
||||
|
||||
from logbook import Logger
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
@@ -28,6 +29,7 @@ from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, Modi
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.mutator import Mutator
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.default import DEFAULT
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
@@ -90,6 +92,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__charge = None
|
||||
|
||||
self.projected = False
|
||||
self.projectionRange = None
|
||||
self.state = FittingModuleState.ONLINE
|
||||
self.build()
|
||||
|
||||
@@ -837,7 +840,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.itemModifiedAttributes.clear()
|
||||
self.chargeModifiedAttributes.clear()
|
||||
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, gang=False):
|
||||
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, gang=False, forcedProjRange=DEFAULT):
|
||||
# We will run the effect when two conditions are met:
|
||||
# 1: It makes sense to run the effect
|
||||
# The effect is either offline
|
||||
@@ -854,6 +857,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
context = ("module",)
|
||||
projected = False
|
||||
|
||||
projectionRange = self.projectionRange if forcedProjRange is DEFAULT else forcedProjRange
|
||||
|
||||
if self.charge is not None:
|
||||
# fix for #82 and it's regression #106
|
||||
if not projected or (self.projected and not forceProjected) or gang:
|
||||
@@ -867,13 +872,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
(not gang or (gang and effect.isType("gang")))
|
||||
):
|
||||
contexts = ("moduleCharge",)
|
||||
# For gang effects, we pass in the effect itself as an argument. However, to avoid going through all
|
||||
# the effect definitions and defining this argument, do a simple try/catch here and be done with it.
|
||||
# @todo: possibly fix this
|
||||
try:
|
||||
effect.handler(fit, self, contexts, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, contexts)
|
||||
effect.handler(fit, self, contexts, projectionRange, effect=effect)
|
||||
|
||||
if self.item:
|
||||
if self.state >= FittingModuleState.OVERHEATED:
|
||||
@@ -883,7 +882,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
and not forceProjected \
|
||||
and effect.activeByDefault \
|
||||
and ((gang and effect.isType("gang")) or not gang):
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
|
||||
for effect in self.item.effects.values():
|
||||
if effect.runTime == runTime and \
|
||||
@@ -893,10 +892,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
(effect.isType("active") and self.state >= FittingModuleState.ACTIVE)) \
|
||||
and ((projected and effect.isType("projected")) or not projected) \
|
||||
and ((gang and effect.isType("gang")) or not gang):
|
||||
try:
|
||||
effect.handler(fit, self, context, effect=effect)
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
effect.handler(fit, self, context, projectionRange, effect=effect)
|
||||
|
||||
def getCycleParameters(self, reloadOverride=None):
|
||||
"""Copied from new eos as well"""
|
||||
@@ -1027,6 +1023,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copy.state = self.state
|
||||
copy.spoolType = self.spoolType
|
||||
copy.spoolAmount = self.spoolAmount
|
||||
copy.projectionRange = self.projectionRange
|
||||
|
||||
for x in self.mutators.values():
|
||||
Mutator(copy, x.attribute, x.value)
|
||||
@@ -1036,10 +1033,17 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def rebase(self, item):
|
||||
state = self.state
|
||||
charge = self.charge
|
||||
spoolType = self.spoolType
|
||||
spoolAmount = self.spoolAmount
|
||||
projectionRange = self.projectionRange
|
||||
|
||||
Module.__init__(self, item, self.baseItem, self.mutaplasmid)
|
||||
self.state = state
|
||||
if self.isValidCharge(charge):
|
||||
self.charge = charge
|
||||
self.spoolType = spoolType
|
||||
self.spoolAmount = spoolAmount
|
||||
self.projectionRange = projectionRange
|
||||
for x in self.mutators.values():
|
||||
Mutator(self, x.attribute, x.value)
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
# skillbook modifiers will use the stale modifier value
|
||||
# GH issue #351
|
||||
fit.register(self)
|
||||
effect.handler(fit, self, ("ship",))
|
||||
effect.handler(fit, self, ("ship",), None, effect=effect)
|
||||
|
||||
def validateModeItem(self, item, owner=None):
|
||||
""" Checks if provided item is a valid mode """
|
||||
|
||||
3
eos/utils/default.py
Normal file
3
eos/utils/default.py
Normal file
@@ -0,0 +1,3 @@
|
||||
class DEFAULT:
|
||||
"""Singleton class to signify default argument value."""
|
||||
pass
|
||||
@@ -20,20 +20,7 @@
|
||||
|
||||
import math
|
||||
|
||||
|
||||
def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True):
|
||||
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
|
||||
if distance is None:
|
||||
return 1
|
||||
if srcFalloffRange > 0:
|
||||
# Most modules cannot be activated when at 3x falloff range, with few exceptions like guns
|
||||
if restrictedRange and distance > srcOptimalRange + 3 * srcFalloffRange:
|
||||
return 0
|
||||
return 0.5 ** ((max(0, distance - srcOptimalRange) / srcFalloffRange) ** 2)
|
||||
elif distance <= srcOptimalRange:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
from service.settings import GraphSettings
|
||||
|
||||
|
||||
# Just copy-paste penalization chain calculation code (with some modifications,
|
||||
@@ -63,3 +50,19 @@ def calculateMultiplier(multipliers):
|
||||
bonus = l[i]
|
||||
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
|
||||
return val
|
||||
|
||||
|
||||
def checkLockRange(src, distance):
|
||||
if distance is None:
|
||||
return True
|
||||
if GraphSettings.getInstance().get('ignoreLockRange'):
|
||||
return True
|
||||
return distance <= src.item.maxTargetRange
|
||||
|
||||
|
||||
def checkDroneControlRange(src, distance):
|
||||
if distance is None:
|
||||
return True
|
||||
if GraphSettings.getInstance().get('ignoreDCR'):
|
||||
return True
|
||||
return distance <= src.item.extraAttributes['droneControlRange']
|
||||
|
||||
@@ -21,37 +21,47 @@
|
||||
import math
|
||||
from functools import lru_cache
|
||||
|
||||
from eos.calc import calculateRangeFactor
|
||||
from eos.const import FittingHardpoint
|
||||
from eos.utils.float import floatUnerr
|
||||
from graphs.calc import calculateRangeFactor
|
||||
from graphs.calc import checkLockRange, checkDroneControlRange
|
||||
from service.attribute import Attribute
|
||||
from service.const import GraphDpsDroneMode
|
||||
from service.settings import GraphSettings
|
||||
|
||||
|
||||
def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
applicationMap = {}
|
||||
for mod in src.item.activeModulesIter():
|
||||
if not mod.isDealingDamage():
|
||||
continue
|
||||
if mod.hardpoint == FittingHardpoint.TURRET:
|
||||
applicationMap[mod] = getTurretMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
atkSpeed=atkSpeed,
|
||||
atkAngle=atkAngle,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtAngle=tgtAngle,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
if inLockRange:
|
||||
applicationMap[mod] = getTurretMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
atkSpeed=atkSpeed,
|
||||
atkAngle=atkAngle,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtAngle=tgtAngle,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[mod] = 0
|
||||
elif mod.hardpoint == FittingHardpoint.MISSILE:
|
||||
applicationMap[mod] = getLauncherMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
# FoF missiles can shoot beyond lock range
|
||||
if inLockRange or (mod.charge is not None and 'fofMissileLaunching' in mod.charge.effects):
|
||||
applicationMap[mod] = getLauncherMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[mod] = 0
|
||||
elif mod.item.group.name in ('Smart Bomb', 'Structure Area Denial Module'):
|
||||
applicationMap[mod] = getSmartbombMult(
|
||||
mod=mod,
|
||||
@@ -64,44 +74,58 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
elif mod.item.group.name == 'Structure Guided Bomb Launcher':
|
||||
applicationMap[mod] = getGuidedBombMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
if inLockRange:
|
||||
applicationMap[mod] = getGuidedBombMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[mod] = 0
|
||||
elif mod.item.group.name in ('Super Weapon', 'Structure Doomsday Weapon'):
|
||||
applicationMap[mod] = getDoomsdayMult(
|
||||
mod=mod,
|
||||
tgt=tgt,
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
# Only single-target DDs need locks
|
||||
if not inLockRange and {'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar', 'lightningWeapon'}.intersection(mod.item.effects):
|
||||
applicationMap[mod] = 0
|
||||
else:
|
||||
applicationMap[mod] = getDoomsdayMult(
|
||||
mod=mod,
|
||||
tgt=tgt,
|
||||
distance=distance,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
for drone in src.item.activeDronesIter():
|
||||
if not drone.isDealingDamage():
|
||||
continue
|
||||
applicationMap[drone] = getDroneMult(
|
||||
drone=drone,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
atkSpeed=atkSpeed,
|
||||
atkAngle=atkAngle,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtAngle=tgtAngle,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
if inLockRange and inDroneRange:
|
||||
applicationMap[drone] = getDroneMult(
|
||||
drone=drone,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
atkSpeed=atkSpeed,
|
||||
atkAngle=atkAngle,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtAngle=tgtAngle,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[drone] = 0
|
||||
for fighter in src.item.activeFightersIter():
|
||||
if not fighter.isDealingDamage():
|
||||
continue
|
||||
for ability in fighter.abilities:
|
||||
if not ability.dealsDamage or not ability.active:
|
||||
continue
|
||||
applicationMap[(fighter, ability.effectID)] = getFighterAbilityMult(
|
||||
fighter=fighter,
|
||||
ability=ability,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
# Bomb launching doesn't need locks
|
||||
if inLockRange or ability.effect.name == 'fighterAbilityLaunchBomb':
|
||||
applicationMap[(fighter, ability.effectID)] = getFighterAbilityMult(
|
||||
fighter=fighter,
|
||||
ability=ability,
|
||||
src=src,
|
||||
tgt=tgt,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
else:
|
||||
applicationMap[(fighter, ability.effectID)] = 0
|
||||
# Ensure consistent results - round off a little to avoid float errors
|
||||
for k, v in applicationMap.items():
|
||||
applicationMap[k] = floatUnerr(v)
|
||||
@@ -200,7 +224,11 @@ def getGuidedBombMult(mod, src, distance, tgtSigRadius):
|
||||
|
||||
|
||||
def getDroneMult(drone, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius):
|
||||
if distance is not None and distance > src.item.extraAttributes['droneControlRange']:
|
||||
if (
|
||||
distance is not None and (
|
||||
(not GraphSettings.getInstance().get('ignoreDCR') and distance > src.item.extraAttributes['droneControlRange']) or
|
||||
(not GraphSettings.getInstance().get('ignoreLockRange') and distance > src.item.maxTargetRange))
|
||||
):
|
||||
return 0
|
||||
droneSpeed = drone.getModifiedItemAttr('maxVelocity')
|
||||
# Hard to simulate drone behavior, so assume chance to hit is 1 for mobile drones
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
|
||||
import math
|
||||
|
||||
from eos.calc import calculateRangeFactor
|
||||
from eos.utils.float import floatUnerr
|
||||
from graphs.calc import calculateRangeFactor
|
||||
from graphs.calc import checkLockRange, checkDroneControlRange
|
||||
from service.const import GraphDpsDroneMode
|
||||
from service.settings import GraphSettings
|
||||
|
||||
@@ -75,26 +76,29 @@ def getTackledSpeed(src, tgt, currentUntackledSpeed, srcScramRange, tgtScrammabl
|
||||
# What's immobile cannot be slowed
|
||||
if maxUntackledSpeed == 0:
|
||||
return maxUntackledSpeed
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
speedRatio = currentUntackledSpeed / maxUntackledSpeed
|
||||
# No scrams or distance is longer than longest scram - nullify scrammables list
|
||||
if srcScramRange is None or (distance is not None and distance > srcScramRange):
|
||||
if not inLockRange or srcScramRange is None or (distance is not None and distance > srcScramRange):
|
||||
tgtScrammables = ()
|
||||
appliedMultipliers = {}
|
||||
# Modules first, they are applied always the same way
|
||||
for wData in webMods:
|
||||
appliedBoost = wData.boost * calculateRangeFactor(
|
||||
srcOptimalRange=wData.optimal,
|
||||
srcFalloffRange=wData.falloff,
|
||||
distance=distance)
|
||||
if appliedBoost:
|
||||
appliedMultipliers.setdefault(wData.stackingGroup, []).append((1 + appliedBoost / 100, wData.resAttrID))
|
||||
# Modules first, they are always applied the same way
|
||||
if inLockRange:
|
||||
for wData in webMods:
|
||||
appliedBoost = wData.boost * calculateRangeFactor(
|
||||
srcOptimalRange=wData.optimal,
|
||||
srcFalloffRange=wData.falloff,
|
||||
distance=distance)
|
||||
if appliedBoost:
|
||||
appliedMultipliers.setdefault(wData.stackingGroup, []).append((1 + appliedBoost / 100, wData.resAttrID))
|
||||
maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables)
|
||||
currentTackledSpeed = maxTackledSpeed * speedRatio
|
||||
# Drones and fighters
|
||||
mobileWebs = []
|
||||
mobileWebs.extend(webFighters)
|
||||
# Drones have range limit
|
||||
if distance is None or distance <= src.item.extraAttributes['droneControlRange']:
|
||||
if inLockRange:
|
||||
mobileWebs.extend(webFighters)
|
||||
if inLockRange and inDroneRange:
|
||||
mobileWebs.extend(webDrones)
|
||||
atkRadius = src.getRadius()
|
||||
# As mobile webs either follow the target or stick to the attacking ship,
|
||||
@@ -138,24 +142,27 @@ def getSigRadiusMult(src, tgt, tgtSpeed, srcScramRange, tgtScrammables, tpMods,
|
||||
# Can blow non-immune ships and target profiles
|
||||
if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'):
|
||||
return 1
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
initSig = tgt.getSigRadius()
|
||||
# No scrams or distance is longer than longest scram - nullify scrammables list
|
||||
if srcScramRange is None or (distance is not None and distance > srcScramRange):
|
||||
if not inLockRange or srcScramRange is None or (distance is not None and distance > srcScramRange):
|
||||
tgtScrammables = ()
|
||||
# TPing modules
|
||||
appliedMultipliers = {}
|
||||
for tpData in tpMods:
|
||||
appliedBoost = tpData.boost * calculateRangeFactor(
|
||||
srcOptimalRange=tpData.optimal,
|
||||
srcFalloffRange=tpData.falloff,
|
||||
distance=distance)
|
||||
if appliedBoost:
|
||||
appliedMultipliers.setdefault(tpData.stackingGroup, []).append((1 + appliedBoost / 100, tpData.resAttrID))
|
||||
if inLockRange:
|
||||
for tpData in tpMods:
|
||||
appliedBoost = tpData.boost * calculateRangeFactor(
|
||||
srcOptimalRange=tpData.optimal,
|
||||
srcFalloffRange=tpData.falloff,
|
||||
distance=distance)
|
||||
if appliedBoost:
|
||||
appliedMultipliers.setdefault(tpData.stackingGroup, []).append((1 + appliedBoost / 100, tpData.resAttrID))
|
||||
# TPing drones
|
||||
mobileTps = []
|
||||
mobileTps.extend(tpFighters)
|
||||
# Drones have range limit
|
||||
if distance is None or distance <= src.item.extraAttributes['droneControlRange']:
|
||||
if inLockRange:
|
||||
mobileTps.extend(tpFighters)
|
||||
if inLockRange and inDroneRange:
|
||||
mobileTps.extend(tpDrones)
|
||||
droneOpt = GraphSettings.getInstance().get('mobileDroneMode')
|
||||
atkRadius = src.getRadius()
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
|
||||
import math
|
||||
|
||||
from graphs.calc import calculateMultiplier, calculateRangeFactor
|
||||
from eos.calc import calculateRangeFactor
|
||||
from graphs.calc import calculateMultiplier, checkLockRange, checkDroneControlRange
|
||||
from graphs.data.base import SmoothPointGetter
|
||||
|
||||
|
||||
@@ -37,33 +38,37 @@ class Distance2NeutingStrGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
neuts.append((
|
||||
mod.getModifiedItemAttr('energyNeutralizerAmount') / self.__getDuration(mod) * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0))
|
||||
mod.maxRange or 0, mod.falloff or 0, True, False))
|
||||
if 'energyNosferatuFalloff' in mod.item.effects and mod.getModifiedItemAttr('nosOverride'):
|
||||
neuts.append((
|
||||
mod.getModifiedItemAttr('powerTransferAmount') / self.__getDuration(mod) * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0))
|
||||
mod.maxRange or 0, mod.falloff or 0, True, False))
|
||||
if 'doomsdayAOENeut' in mod.item.effects:
|
||||
neuts.append((
|
||||
mod.getModifiedItemAttr('energyNeutralizerAmount') / self.__getDuration(mod) * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0))
|
||||
mod.falloff or 0, False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'entityEnergyNeutralizerFalloff' in drone.item.effects:
|
||||
neuts.extend(drone.amountActive * ((
|
||||
drone.getModifiedItemAttr('energyNeutralizerAmount') / (drone.getModifiedItemAttr('energyNeutralizerDuration') / 1000) * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0),))
|
||||
math.inf, 0, True, True),))
|
||||
for fighter, ability in src.item.activeFighterAbilityIter():
|
||||
if ability.effect.name == 'fighterAbilityEnergyNeutralizer':
|
||||
nps = fighter.getModifiedItemAttr('fighterAbilityEnergyNeutralizerAmount') / (ability.cycleTime / 1000)
|
||||
neuts.append((
|
||||
nps * fighter.amount * resonance,
|
||||
math.inf, 0))
|
||||
math.inf, 0, True, False))
|
||||
return {'neuts': neuts}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
combinedStr = 0
|
||||
for strength, optimal, falloff in commonData['neuts']:
|
||||
for strength, optimal, falloff, needsLock, needsDcr in commonData['neuts']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
combinedStr += strength * calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
return combinedStr
|
||||
|
||||
@@ -84,28 +89,32 @@ class Distance2WebbingStrGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
webs.append((
|
||||
mod.getModifiedItemAttr('speedFactor') * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default'))
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default', True, False))
|
||||
if 'doomsdayAOEWeb' in mod.item.effects:
|
||||
webs.append((
|
||||
mod.getModifiedItemAttr('speedFactor') * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0, 'default'))
|
||||
mod.falloff or 0, 'default', False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'remoteWebifierEntity' in drone.item.effects:
|
||||
webs.extend(drone.amountActive * ((
|
||||
drone.getModifiedItemAttr('speedFactor') * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0, 'default'),))
|
||||
math.inf, 0, 'default', True, True),))
|
||||
for fighter, ability in src.item.activeFighterAbilityIter():
|
||||
if ability.effect.name == 'fighterAbilityStasisWebifier':
|
||||
webs.append((
|
||||
fighter.getModifiedItemAttr('fighterAbilityStasisWebifierSpeedPenalty') * fighter.amount * resonance,
|
||||
math.inf, 0, 'default'))
|
||||
math.inf, 0, 'default', True, False))
|
||||
return {'webs': webs}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
strMults = {}
|
||||
for strength, optimal, falloff, stackingGroup in commonData['webs']:
|
||||
for strength, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['webs']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
strength *= calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
strMults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
|
||||
strMult = calculateMultiplier(strMults)
|
||||
@@ -129,28 +138,32 @@ class Distance2EcmStrMaxGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
ecms.append((
|
||||
max(mod.getModifiedItemAttr(a) for a in self.ECM_ATTRS_GENERAL) * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0))
|
||||
mod.maxRange or 0, mod.falloff or 0, True, False))
|
||||
if 'doomsdayAOEECM' in mod.item.effects:
|
||||
ecms.append((
|
||||
max(mod.getModifiedItemAttr(a) for a in self.ECM_ATTRS_GENERAL) * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0))
|
||||
mod.falloff or 0, False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'entityECMFalloff' in drone.item.effects:
|
||||
ecms.extend(drone.amountActive * ((
|
||||
max(drone.getModifiedItemAttr(a) for a in self.ECM_ATTRS_GENERAL) * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0),))
|
||||
math.inf, 0, True, True),))
|
||||
for fighter, ability in src.item.activeFighterAbilityIter():
|
||||
if ability.effect.name == 'fighterAbilityECM':
|
||||
ecms.append((
|
||||
max(fighter.getModifiedItemAttr(a) for a in self.ECM_ATTRS_FIGHTERS) * fighter.amount * resonance,
|
||||
math.inf, 0))
|
||||
math.inf, 0, True, False))
|
||||
return {'ecms': ecms}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
combinedStr = 0
|
||||
for strength, optimal, falloff in commonData['ecms']:
|
||||
for strength, optimal, falloff, needsLock, needsDcr in commonData['ecms']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
combinedStr += strength * calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
return combinedStr
|
||||
|
||||
@@ -168,23 +181,27 @@ class Distance2DampStrLockRangeGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
damps.append((
|
||||
mod.getModifiedItemAttr('maxTargetRangeBonus') * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default'))
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default', True, False))
|
||||
if 'doomsdayAOEDamp' in mod.item.effects:
|
||||
damps.append((
|
||||
mod.getModifiedItemAttr('maxTargetRangeBonus') * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0, 'default'))
|
||||
mod.falloff or 0, 'default', False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'remoteSensorDampEntity' in drone.item.effects:
|
||||
damps.extend(drone.amountActive * ((
|
||||
drone.getModifiedItemAttr('maxTargetRangeBonus') * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0, 'default'),))
|
||||
math.inf, 0, 'default', True, True),))
|
||||
return {'damps': damps}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
strMults = {}
|
||||
for strength, optimal, falloff, stackingGroup in commonData['damps']:
|
||||
for strength, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['damps']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
strength *= calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
strMults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
|
||||
strMult = calculateMultiplier(strMults)
|
||||
@@ -205,23 +222,27 @@ class Distance2TdStrOptimalGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
tds.append((
|
||||
mod.getModifiedItemAttr('maxRangeBonus') * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default'))
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default', True, False))
|
||||
if 'doomsdayAOETrack' in mod.item.effects:
|
||||
tds.append((
|
||||
mod.getModifiedItemAttr('maxRangeBonus') * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0, 'default'))
|
||||
mod.falloff or 0, 'default', False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'npcEntityWeaponDisruptor' in drone.item.effects:
|
||||
tds.extend(drone.amountActive * ((
|
||||
drone.getModifiedItemAttr('maxRangeBonus') * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0, 'default'),))
|
||||
math.inf, 0, 'default', True, True),))
|
||||
return {'tds': tds}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
strMults = {}
|
||||
for strength, optimal, falloff, stackingGroup in commonData['tds']:
|
||||
for strength, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['tds']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
strength *= calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
strMults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
|
||||
strMult = calculateMultiplier(strMults)
|
||||
@@ -243,20 +264,24 @@ class Distance2GdStrRangeGetter(SmoothPointGetter):
|
||||
gds.append((
|
||||
mod.getModifiedItemAttr('missileVelocityBonus') * resonance,
|
||||
mod.getModifiedItemAttr('explosionDelayBonus') * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default'))
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default', True, False))
|
||||
if 'doomsdayAOETrack' in mod.item.effects:
|
||||
gds.append((
|
||||
mod.getModifiedItemAttr('missileVelocityBonus') * resonance,
|
||||
mod.getModifiedItemAttr('explosionDelayBonus') * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0, 'default'))
|
||||
mod.falloff or 0, 'default', False, False))
|
||||
return {'gds': gds}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
velocityStrMults = {}
|
||||
timeStrMults = {}
|
||||
for velocityStr, timeStr, optimal, falloff, stackingGroup in commonData['gds']:
|
||||
for velocityStr, timeStr, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['gds']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
rangeFactor = calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
velocityStr *= rangeFactor
|
||||
timeStr *= rangeFactor
|
||||
@@ -281,23 +306,27 @@ class Distance2TpStrGetter(SmoothPointGetter):
|
||||
if effectName in mod.item.effects:
|
||||
tps.append((
|
||||
mod.getModifiedItemAttr('signatureRadiusBonus') * resonance,
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default'))
|
||||
mod.maxRange or 0, mod.falloff or 0, 'default', True, False))
|
||||
if 'doomsdayAOEPaint' in mod.item.effects:
|
||||
tps.append((
|
||||
mod.getModifiedItemAttr('signatureRadiusBonus') * resonance,
|
||||
max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()),
|
||||
mod.falloff or 0, 'default'))
|
||||
mod.falloff or 0, 'default', False, False))
|
||||
for drone in src.item.activeDronesIter():
|
||||
if 'remoteTargetPaintEntity' in drone.item.effects:
|
||||
tps.extend(drone.amountActive * ((
|
||||
drone.getModifiedItemAttr('signatureRadiusBonus') * resonance,
|
||||
src.item.extraAttributes['droneControlRange'], 0, 'default'),))
|
||||
math.inf, 0, 'default', True, True),))
|
||||
return {'tps': tps}
|
||||
|
||||
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
|
||||
distance = x
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
strMults = {}
|
||||
for strength, optimal, falloff, stackingGroup in commonData['tps']:
|
||||
for strength, optimal, falloff, stackingGroup, needsLock, needsDcr in commonData['tps']:
|
||||
if (needsLock and not inLockRange) or (needsDcr and not inDroneRange):
|
||||
continue
|
||||
strength *= calculateRangeFactor(srcOptimalRange=optimal, srcFalloffRange=falloff, distance=distance)
|
||||
strMults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
|
||||
strMult = calculateMultiplier(strMults)
|
||||
|
||||
@@ -18,23 +18,32 @@
|
||||
# =============================================================================
|
||||
|
||||
|
||||
from eos.calc import calculateRangeFactor
|
||||
from eos.utils.float import floatUnerr
|
||||
from graphs.calc import calculateRangeFactor
|
||||
from graphs.calc import checkLockRange, checkDroneControlRange
|
||||
|
||||
|
||||
def getApplicationPerKey(src, distance):
|
||||
inLockRange = checkLockRange(src=src, distance=distance)
|
||||
inDroneRange = checkDroneControlRange(src=src, distance=distance)
|
||||
applicationMap = {}
|
||||
for mod in src.item.activeModulesIter():
|
||||
if not mod.isRemoteRepping():
|
||||
continue
|
||||
applicationMap[mod] = 1 if distance is None else calculateRangeFactor(
|
||||
srcOptimalRange=mod.maxRange or 0,
|
||||
srcFalloffRange=mod.falloff or 0,
|
||||
distance=distance)
|
||||
if not inLockRange:
|
||||
applicationMap[mod] = 0
|
||||
else:
|
||||
applicationMap[mod] = calculateRangeFactor(
|
||||
srcOptimalRange=mod.maxRange or 0,
|
||||
srcFalloffRange=mod.falloff or 0,
|
||||
distance=distance)
|
||||
for drone in src.item.activeDronesIter():
|
||||
if not drone.isRemoteRepping():
|
||||
continue
|
||||
applicationMap[drone] = 1 if distance is None or distance <= src.item.extraAttributes['droneControlRange'] else 0
|
||||
if not inLockRange or not inDroneRange:
|
||||
applicationMap[drone] = 0
|
||||
else:
|
||||
applicationMap[drone] = 1
|
||||
# Ensure consistent results - round off a little to avoid float errors
|
||||
for k, v in applicationMap.items():
|
||||
applicationMap[k] = floatUnerr(v)
|
||||
|
||||
@@ -116,9 +116,7 @@ class GraphFrame(AuxiliaryFrame):
|
||||
self.SetMinSize(newSize)
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -152,10 +152,10 @@ class BaseWrapperList(gui.display.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
self.removeWrappers(self.getSelectedWrappers())
|
||||
event.Skip()
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinAdditionPanes.boosterView import BoosterView
|
||||
from gui.builtinAdditionPanes.cargoView import CargoView
|
||||
@@ -35,9 +36,10 @@ from gui.toggle_panel import TogglePanel
|
||||
|
||||
class AdditionsPane(TogglePanel):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent, mainFrame):
|
||||
|
||||
TogglePanel.__init__(self, parent, force_layout=1)
|
||||
self.mainFrame = mainFrame
|
||||
|
||||
self.SetLabel("Additions")
|
||||
pane = self.GetContentPanel()
|
||||
@@ -45,7 +47,7 @@ class AdditionsPane(TogglePanel):
|
||||
baseSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
pane.SetSizer(baseSizer)
|
||||
|
||||
self.notebook = ChromeNotebook(pane, False)
|
||||
self.notebook = ChromeNotebook(pane, can_add=False, tabWidthMode=1)
|
||||
self.notebook.SetMinSize((-1, 1000))
|
||||
|
||||
baseSizer.Add(self.notebook, 1, wx.EXPAND)
|
||||
@@ -83,6 +85,9 @@ class AdditionsPane(TogglePanel):
|
||||
self.notes = NotesView(self.notebook)
|
||||
self.notebook.AddPage(self.notes, "Notes", image=notesImg, closeable=False)
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.OnFitChanged)
|
||||
self.mainFrame.Bind(GE.FIT_NOTES_CHANGED, self.OnNotesChanged)
|
||||
|
||||
self.notebook.SetSelection(0)
|
||||
|
||||
PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Command", "Notes"]
|
||||
@@ -106,3 +111,25 @@ class AdditionsPane(TogglePanel):
|
||||
self.parent.SetSashInvisible(False)
|
||||
self.parent.SetMinimumPaneSize(200)
|
||||
self.parent.SetSashPosition(self.old_pos, True)
|
||||
|
||||
def OnFitChanged(self, event):
|
||||
event.Skip()
|
||||
activeFitID = self.mainFrame.getActiveFit()
|
||||
if activeFitID is not None and activeFitID not in event.fitIDs:
|
||||
return
|
||||
self.updateExtraText()
|
||||
|
||||
def OnNotesChanged(self, event):
|
||||
event.Skip()
|
||||
self.updateExtraText()
|
||||
|
||||
def updateExtraText(self):
|
||||
refresh = False
|
||||
for i in range(self.notebook.GetPageCount()):
|
||||
page = self.notebook.GetPage(i)
|
||||
if hasattr(page, 'getTabExtraText'):
|
||||
refresh = True
|
||||
self.notebook.SetPageTitleExtra(i, page.getTabExtraText() or '', refresh=False)
|
||||
if refresh:
|
||||
self.notebook.tabs_container.AdjustTabsSize()
|
||||
self.notebook.Refresh()
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import io
|
||||
import os.path
|
||||
import zipfile
|
||||
from collections import OrderedDict
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -32,15 +33,12 @@ pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class BitmapLoader:
|
||||
# try:
|
||||
# archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
|
||||
# logging.info("Using zipped image files.")
|
||||
# except (IOError, TypeError):
|
||||
# logging.info("Using local image files.")
|
||||
# archive = None
|
||||
|
||||
pyfalog.info("Using local image files.")
|
||||
archive = None
|
||||
try:
|
||||
archive = zipfile.ZipFile(config.imgsZIP, 'r')
|
||||
pyfalog.info("Using zipped image files.")
|
||||
except (IOError, TypeError):
|
||||
pyfalog.info("Using local image files.")
|
||||
archive = None
|
||||
|
||||
cached_bitmaps = OrderedDict()
|
||||
dont_use_cached_bitmaps = False
|
||||
@@ -131,8 +129,8 @@ class BitmapLoader:
|
||||
|
||||
try:
|
||||
img_data = cls.archive.read(path)
|
||||
sbuf = io.StringIO(img_data)
|
||||
return wx.ImageFromStream(sbuf)
|
||||
bbuf = io.BytesIO(img_data)
|
||||
return wx.Image(bbuf)
|
||||
except KeyError:
|
||||
pyfalog.warning("Missing icon file from zip: {0}".format(path))
|
||||
else:
|
||||
|
||||
@@ -88,12 +88,12 @@ class BoosterView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
boosters = self.getSelectedBoosters()
|
||||
self.removeBoosters(boosters)
|
||||
event.Skip()
|
||||
@@ -226,3 +226,23 @@ class BoosterView(d.Display):
|
||||
continue
|
||||
boosters.append(booster)
|
||||
return boosters
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active boosters
|
||||
if opt == 1:
|
||||
amount = len([b for b in fit.boosters if b.active])
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of boosters
|
||||
elif opt == 2:
|
||||
amount = len(fit.boosters)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -104,12 +104,12 @@ class CargoView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
cargos = self.getSelectedCargos()
|
||||
self.removeCargos(cargos)
|
||||
event.Skip()
|
||||
@@ -214,3 +214,19 @@ class CargoView(d.Display):
|
||||
continue
|
||||
cargos.append(cargo)
|
||||
return cargos
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Total amount of cargo items
|
||||
if opt in (1, 2):
|
||||
amount = len(fit.cargo)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -102,12 +102,12 @@ class CommandView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
commandFits = self.getSelectedCommandFits()
|
||||
self.removeCommandFits(commandFits)
|
||||
event.Skip()
|
||||
@@ -247,3 +247,27 @@ class CommandView(d.Display):
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCommandFitsCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
commandFitIDs=fitIDs))
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active command fits
|
||||
if opt == 1:
|
||||
amount = 0
|
||||
for commandFit in fit.commandFits:
|
||||
info = commandFit.getCommandInfo(fitID)
|
||||
if info is not None and info.active:
|
||||
amount += 1
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of command fits
|
||||
elif opt == 2:
|
||||
amount = len(fit.commandFits)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -120,12 +120,12 @@ class DroneView(Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
drones = self.getSelectedDrones()
|
||||
self.removeDroneStacks(drones)
|
||||
event.Skip()
|
||||
@@ -260,7 +260,7 @@ class DroneView(Display):
|
||||
drone = self.drones[row]
|
||||
except IndexError:
|
||||
return
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if event.GetModifiers() == wx.MOD_ALT:
|
||||
self.removeDroneStacks([drone])
|
||||
else:
|
||||
self.removeDrone(drone)
|
||||
@@ -337,3 +337,27 @@ class DroneView(Display):
|
||||
continue
|
||||
drones.append(drone)
|
||||
return drones
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active drones
|
||||
if opt == 1:
|
||||
amount = 0
|
||||
for droneStack in fit.drones:
|
||||
amount += droneStack.amountActive
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of drones
|
||||
elif opt == 2:
|
||||
amount = 0
|
||||
for droneStack in fit.drones:
|
||||
amount += droneStack.amount
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -117,6 +117,26 @@ class FighterView(wx.Panel):
|
||||
|
||||
self.Refresh()
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active fighter squads
|
||||
if opt == 1:
|
||||
amount = len([f for f in fit.fighters if f.active])
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of fighter squads
|
||||
elif opt == 2:
|
||||
amount = len(fit.fighters)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class FighterDisplay(d.Display):
|
||||
|
||||
@@ -185,12 +205,12 @@ class FighterDisplay(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
fighters = self.getSelectedFighters()
|
||||
self.removeFighters(fighters)
|
||||
event.Skip()
|
||||
@@ -295,12 +315,11 @@ class FighterDisplay(d.Display):
|
||||
if row != -1:
|
||||
col = self.getColumn(event.Position)
|
||||
if col != self.getColIndex(State):
|
||||
mstate = wx.GetMouseState()
|
||||
try:
|
||||
fighter = self.fighters[row]
|
||||
except IndexError:
|
||||
return
|
||||
if mstate.GetModifiers() == wx.MOD_ALT:
|
||||
if event.GetModifiers() == wx.MOD_ALT:
|
||||
fighters = getSimilarFighters(self.original, fighter)
|
||||
else:
|
||||
fighters = [fighter]
|
||||
|
||||
@@ -101,6 +101,25 @@ class ImplantView(wx.Panel):
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeImplantLocationCommand(
|
||||
fitID=fitID, source=ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER))
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active implants
|
||||
if opt == 1:
|
||||
amount = len([i for i in fit.appliedImplants if i.active])
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of implants
|
||||
elif opt == 2:
|
||||
amount = len(fit.appliedImplants)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
class ImplantDisplay(d.Display):
|
||||
|
||||
@@ -143,12 +162,12 @@ class ImplantDisplay(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
implants = self.getSelectedImplants()
|
||||
self.removeImplants(implants)
|
||||
event.Skip()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from service.fit import Fit
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.utils.helpers_wxPython import HandleCtrlBackspace
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class NotesView(wx.Panel):
|
||||
@@ -14,14 +15,14 @@ class NotesView(wx.Panel):
|
||||
self.lastFitId = None
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.editNotes = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.BORDER_NONE, )
|
||||
mainSizer.Add(self.editNotes, 1, wx.EXPAND)
|
||||
self.editNotes = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.BORDER_NONE)
|
||||
mainSizer.Add(self.editNotes, 1, wx.EXPAND | wx.ALL, 10)
|
||||
self.SetSizer(mainSizer)
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.Bind(wx.EVT_TEXT, self.onText)
|
||||
self.editNotes.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
|
||||
self.saveTimer = wx.Timer(self)
|
||||
self.Bind(wx.EVT_TIMER, self.delayedSave, self.saveTimer)
|
||||
self.changeTimer = wx.Timer(self)
|
||||
self.Bind(wx.EVT_TIMER, self.delayedSave, self.changeTimer)
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
@@ -38,7 +39,7 @@ class NotesView(wx.Panel):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(activeFitID)
|
||||
|
||||
self.saveTimer.Stop() # cancel any pending timers
|
||||
self.changeTimer.Stop() # cancel any pending timers
|
||||
|
||||
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
|
||||
|
||||
@@ -51,13 +52,32 @@ class NotesView(wx.Panel):
|
||||
return
|
||||
elif activeFitID != self.lastFitId:
|
||||
self.lastFitId = activeFitID
|
||||
self.editNotes.SetValue(fit.notes or "")
|
||||
self.editNotes.ChangeValue(fit.notes or "")
|
||||
wx.PostEvent(self.mainFrame, GE.FitNotesChanged())
|
||||
|
||||
def onText(self, event):
|
||||
# delay the save so we're not writing to sqlite on every keystroke
|
||||
self.saveTimer.Stop() # cancel the existing timer
|
||||
self.saveTimer.Start(1000, True)
|
||||
self.changeTimer.Stop() # cancel the existing timer
|
||||
self.changeTimer.Start(1000, True)
|
||||
|
||||
def delayedSave(self, event):
|
||||
event.Skip()
|
||||
sFit = Fit.getInstance()
|
||||
sFit.editNotes(self.lastFitId, self.editNotes.GetValue())
|
||||
wx.PostEvent(self.mainFrame, GE.FitNotesChanged())
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active implants
|
||||
if opt in (1, 2):
|
||||
amount = len(self.editNotes.GetValue())
|
||||
return ' ({})'.format(formatAmount(amount, 2, 0, 3)) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -27,6 +27,7 @@ import gui.builtinAdditionPanes.droneView
|
||||
import gui.display as d
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
from eos.const import FittingModuleState
|
||||
from eos.saveddata.drone import Drone as EosDrone
|
||||
from eos.saveddata.fighter import Fighter as EosFighter
|
||||
from eos.saveddata.fit import Fit as EosFit
|
||||
@@ -74,7 +75,8 @@ class ProjectedView(d.Display):
|
||||
'Ammo Icon',
|
||||
'Base Icon',
|
||||
'Base Name',
|
||||
'Ammo']
|
||||
'Ammo',
|
||||
'Projection Range']
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.BORDER_NONE)
|
||||
@@ -127,12 +129,12 @@ class ProjectedView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
items=self.getSelectedProjectors(),
|
||||
@@ -265,7 +267,7 @@ class ProjectedView(d.Display):
|
||||
selection = self.getSelectedProjectors()
|
||||
if mainItem not in selection:
|
||||
selection = [mainItem]
|
||||
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
|
||||
modPressed = event.GetModifiers() == wx.MOD_ALT
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if isinstance(mainItem, EosModule) and modPressed:
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
@@ -340,7 +342,7 @@ class ProjectedView(d.Display):
|
||||
if mainItem is None:
|
||||
return
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
modPressed = wx.GetMouseState().GetModifiers() == wx.MOD_ALT
|
||||
modPressed = event.GetModifiers() == wx.MOD_ALT
|
||||
if isinstance(mainItem, EosFit):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=[mainItem], amount=math.inf if modPressed else 1))
|
||||
@@ -396,3 +398,34 @@ class ProjectedView(d.Display):
|
||||
fitID=self.mainFrame.getActiveFit(),
|
||||
projectedFitIDs=fitIDs,
|
||||
amount=1))
|
||||
|
||||
def getTabExtraText(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return None
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is None:
|
||||
return None
|
||||
opt = sFit.serviceFittingOptions["additionsLabels"]
|
||||
# Amount of active projected items
|
||||
if opt == 1:
|
||||
amount = 0
|
||||
for projectedFit in fit.projectedFits:
|
||||
info = projectedFit.getProjectionInfo(fitID)
|
||||
if info is not None and info.active:
|
||||
amount += 1
|
||||
amount += len([m for m in fit.projectedModules if m.state > FittingModuleState.OFFLINE])
|
||||
amount += len([d for d in fit.projectedDrones if d.amountActive > 0])
|
||||
amount += len([f for f in fit.projectedFighters if f.active])
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
# Total amount of projected items
|
||||
elif opt == 2:
|
||||
amount = 0
|
||||
amount += len(fit.projectedFits)
|
||||
amount += len(fit.projectedModules)
|
||||
amount += len(fit.projectedDrones)
|
||||
amount += len(fit.projectedFighters)
|
||||
return ' ({})'.format(amount) if amount else None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -22,6 +22,7 @@ from gui.builtinContextMenus import shipJump
|
||||
# Generic item manipulations
|
||||
from gui.builtinContextMenus import itemRemove
|
||||
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
|
||||
@@ -44,8 +45,10 @@ from gui.builtinContextMenus import damagePatternChange
|
||||
from gui.builtinContextMenus import factorReload
|
||||
from gui.builtinContextMenus.targetProfile import switcher
|
||||
# Graph extra options
|
||||
from gui.builtinContextMenus import graphDmgIgnoreResists
|
||||
from gui.builtinContextMenus import graphDmgApplyProjected
|
||||
from gui.builtinContextMenus import graphDmgIgnoreResists
|
||||
from gui.builtinContextMenus import graphLockRange
|
||||
from gui.builtinContextMenus import graphDroneControlRange
|
||||
from gui.builtinContextMenus import graphDmgDroneMode
|
||||
# Additions panel menus
|
||||
from gui.builtinContextMenus import additionsExportSelection
|
||||
|
||||
@@ -69,7 +69,7 @@ class FighterAbilities(ContextMenuCombined):
|
||||
command = cmd.GuiToggleLocalFighterAbilityStateCommand
|
||||
if self.fighter in container:
|
||||
mainPosition = container.index(self.fighter)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fighters = getSimilarFighters(container, self.fighter)
|
||||
else:
|
||||
fighters = self.selection
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from gui.builtinViews.emptyView import BlankPage
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
|
||||
30
gui/builtinContextMenus/graphDroneControlRange.py
Normal file
30
gui/builtinContextMenus/graphDroneControlRange.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.settings import GraphSettings
|
||||
|
||||
|
||||
class GraphIgnoreDcrMenu(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = GraphSettings.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext):
|
||||
return srcContext in ('dmgStatsGraph', 'remoteRepsGraph', 'ewarStatsGraph')
|
||||
|
||||
def getText(self, callingWindow, itmContext):
|
||||
return 'Ignore Drone Control Range'
|
||||
|
||||
def activate(self, callingWindow, fullContext, i):
|
||||
self.settings.set('ignoreDCR', not self.settings.get('ignoreDCR'))
|
||||
wx.PostEvent(self.mainFrame, GE.GraphOptionChanged())
|
||||
|
||||
def isChecked(self, i):
|
||||
return self.settings.get('ignoreDCR')
|
||||
|
||||
|
||||
GraphIgnoreDcrMenu.register()
|
||||
30
gui/builtinContextMenus/graphLockRange.py
Normal file
30
gui/builtinContextMenus/graphLockRange.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenuUnconditional
|
||||
from service.settings import GraphSettings
|
||||
|
||||
|
||||
class GraphIgnoreLockRangeMenu(ContextMenuUnconditional):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = GraphSettings.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext):
|
||||
return srcContext in ('dmgStatsGraph', 'remoteRepsGraph', 'ewarStatsGraph')
|
||||
|
||||
def getText(self, callingWindow, itmContext):
|
||||
return 'Ignore Lock Range'
|
||||
|
||||
def activate(self, callingWindow, fullContext, i):
|
||||
self.settings.set('ignoreLockRange', not self.settings.get('ignoreLockRange'))
|
||||
wx.PostEvent(self.mainFrame, GE.GraphOptionChanged())
|
||||
|
||||
def isChecked(self, i):
|
||||
return self.settings.get('ignoreLockRange')
|
||||
|
||||
|
||||
GraphIgnoreLockRangeMenu.register()
|
||||
125
gui/builtinContextMenus/itemProjectionRange.py
Normal file
125
gui/builtinContextMenus/itemProjectionRange.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import re
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.fighter import Fighter as EosFighter
|
||||
from eos.saveddata.fit import Fit as EosFit
|
||||
from eos.saveddata.module import Module as EosModule
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarFighters, getSimilarModPositions
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class ChangeItemProjectionRange(ContextMenuCombined):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||
if srcContext not in ('projectedFit', 'projectedModule', 'projectedDrone', 'projectedFighter'):
|
||||
return False
|
||||
if mainItem is None:
|
||||
return False
|
||||
if getattr(mainItem, 'isExclusiveSystemEffect', False):
|
||||
return False
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||
return 'Change {} Range'.format(itmContext)
|
||||
|
||||
def activate(self, callingWindow, fullContext, mainItem, selection, i):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if isinstance(mainItem, EosFit):
|
||||
try:
|
||||
value = mainItem.getProjectionInfo(fitID).projectionRange
|
||||
except AttributeError:
|
||||
return
|
||||
else:
|
||||
value = mainItem.projectionRange
|
||||
if value is not None:
|
||||
value /= 1000
|
||||
with RangeChanger(self.mainFrame, value) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
|
||||
if cleanInput:
|
||||
try:
|
||||
cleanInputFloat = float(cleanInput)
|
||||
except ValueError:
|
||||
return
|
||||
newRange = cleanInputFloat * 1000
|
||||
else:
|
||||
newRange = None
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
items = selection
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
if isinstance(mainItem, EosModule):
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = getSimilarModPositions(fit.projectedModules, mainItem)
|
||||
items = [fit.projectedModules[p] for p in positions]
|
||||
elif isinstance(mainItem, EosFighter):
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
items = getSimilarFighters(fit.projectedFighters, mainItem)
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeProjectedItemsProjectionRangeCommand(
|
||||
fitID=fitID, items=items, projectionRange=newRange))
|
||||
|
||||
|
||||
ChangeItemProjectionRange.register()
|
||||
|
||||
|
||||
class RangeChanger(wx.Dialog):
|
||||
|
||||
def __init__(self, parent, value):
|
||||
super().__init__(parent, title='Change Projection Range', style=wx.DEFAULT_DIALOG_STYLE)
|
||||
self.SetMinSize((346, 156))
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
bSizer2 = wx.BoxSizer(wx.VERTICAL)
|
||||
text = wx.StaticText(self, wx.ID_ANY, 'New Range, km:')
|
||||
bSizer2.Add(text, 0)
|
||||
|
||||
bSizer1.Add(bSizer2, 0, wx.ALL, 10)
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
if value is None:
|
||||
value = ''
|
||||
else:
|
||||
if value == int(value):
|
||||
value = int(value)
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
|
||||
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
# checks to make sure it's valid number
|
||||
@staticmethod
|
||||
def onChar(event):
|
||||
key = event.GetKeyCode()
|
||||
|
||||
acceptable_characters = '1234567890.'
|
||||
acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste
|
||||
if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters):
|
||||
event.Skip()
|
||||
return
|
||||
else:
|
||||
return False
|
||||
@@ -65,7 +65,7 @@ class RemoveItem(ContextMenuCombined):
|
||||
def __handleModule(self, callingWindow, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
positions = getSimilarModPositions(fit.modules, mainItem)
|
||||
else:
|
||||
positions = []
|
||||
@@ -88,7 +88,7 @@ class RemoveItem(ContextMenuCombined):
|
||||
def __handleFighter(self, callingWindow, mainItem, selection):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fighters = getSimilarFighters(fit.fighters, mainItem)
|
||||
else:
|
||||
fighters = selection
|
||||
@@ -131,7 +131,7 @@ class RemoveItem(ContextMenuCombined):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=selection, amount=math.inf))
|
||||
elif isinstance(mainItem, EosModule):
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
positions = getSimilarModPositions(fit.projectedModules, mainItem)
|
||||
items = [fit.projectedModules[p] for p in positions]
|
||||
@@ -143,7 +143,7 @@ class RemoveItem(ContextMenuCombined):
|
||||
self.mainFrame.command.Submit(cmd.GuiRemoveProjectedItemsCommand(
|
||||
fitID=fitID, items=selection, amount=math.inf))
|
||||
elif isinstance(mainItem, EosFighter):
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
items = getSimilarFighters(fit.projectedFighters, mainItem)
|
||||
else:
|
||||
|
||||
@@ -48,10 +48,9 @@ class ItemStats(ContextMenuSingle):
|
||||
if srcContext == "fittingModule" and stuff.isEmpty:
|
||||
return
|
||||
|
||||
mstate = wx.GetMouseState()
|
||||
reuse = False
|
||||
|
||||
if mstate.GetModifiers() == wx.MOD_SHIFT:
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_SHIFT:
|
||||
reuse = True
|
||||
|
||||
if self.mainFrame.GetActiveStatsWindow() is None and reuse:
|
||||
|
||||
@@ -43,6 +43,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
|
||||
self.mainItem = mainItem
|
||||
self.selection = selection
|
||||
self.srcContext = srcContext
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||
@@ -55,9 +56,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
def get_metalevel(x):
|
||||
if 'metaLevel' not in x.attributes:
|
||||
return 0
|
||||
return x.attributes['metaLevel'].value
|
||||
return x.metaLevel or 0
|
||||
|
||||
def get_metagroup(x):
|
||||
# We want deadspace before officer mods
|
||||
@@ -126,7 +125,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
|
||||
self.moduleLookup[id] = item, context
|
||||
m.Append(mitem)
|
||||
mitem.Enable(fit.canFit(item))
|
||||
mitem.Enable(self.srcContext in ('projectedModule', 'projectedDrone', 'projectedFighter') or fit.canFit(item))
|
||||
|
||||
return m
|
||||
|
||||
@@ -153,7 +152,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
def __handleModule(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
positions = getSimilarModPositions(fit.modules, self.mainItem)
|
||||
else:
|
||||
sMkt = Market.getInstance()
|
||||
@@ -192,7 +191,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
def __handleFighter(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fighters = getSimilarFighters(fit.fighters, self.mainItem)
|
||||
else:
|
||||
fighters = self.selection
|
||||
@@ -245,7 +244,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
def __handleProjectedModule(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
positions = getSimilarModPositions(fit.projectedModules, self.mainItem)
|
||||
else:
|
||||
sMkt = Market.getInstance()
|
||||
@@ -282,7 +281,7 @@ class ChangeItemToVariation(ContextMenuCombined):
|
||||
def __handleProjectedFighter(self, varItem):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if wx.GetMouseState().GetModifiers() == wx.MOD_ALT:
|
||||
if wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL):
|
||||
fighters = getSimilarFighters(fit.projectedFighters, self.mainItem)
|
||||
else:
|
||||
fighters = self.selection
|
||||
|
||||
@@ -226,9 +226,8 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
mstate = wx.GetMouseState()
|
||||
# Switch in selection or all modules, depending on modifier key state and settings
|
||||
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not (mstate.GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL))
|
||||
switchAll = sFit.serviceFittingOptions['ammoChangeAll'] is not (wx.GetMouseState().GetModifiers() in (wx.MOD_ALT, wx.MOD_CONTROL))
|
||||
if switchAll:
|
||||
if self.srcContext == 'fittingModule':
|
||||
command = cmd.GuiChangeLocalModuleChargesCommand
|
||||
|
||||
@@ -38,6 +38,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
self.stuff = stuff
|
||||
self.item = item
|
||||
|
||||
@@ -52,6 +53,7 @@ class ItemAffectedBy(wx.Panel):
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.affectedBy = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER)
|
||||
self.affectedBy.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
mainSizer.Add(self.affectedBy, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
|
||||
@@ -21,6 +21,8 @@ class ItemParams(wx.Panel):
|
||||
def __init__(self, parent, stuff, item, context=None):
|
||||
# Had to manually set the size here, otherwise column widths couldn't be calculated correctly. See #1878
|
||||
wx.Panel.__init__(self, parent, size=(1000, 1000))
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = wx.lib.agw.hypertreelist.HyperTreeList(self, wx.ID_ANY, agwStyle=wx.TR_HIDE_ROOT | wx.TR_NO_LINES | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_HAS_BUTTONS)
|
||||
|
||||
@@ -9,7 +9,7 @@ from gui.utils.numberFormatter import formatAmount
|
||||
|
||||
|
||||
def defaultSort(item):
|
||||
return (item.attributes['metaLevel'].value if 'metaLevel' in item.attributes else 0, item.name)
|
||||
return (item.metaLevel or 0, item.name)
|
||||
|
||||
|
||||
class ItemCompare(wx.Panel):
|
||||
@@ -19,6 +19,7 @@ class ItemCompare(wx.Panel):
|
||||
sPrice.getPrices(items, self.UpdateList, fetchTimeout=90)
|
||||
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = AutoListCtrl(self, wx.ID_ANY,
|
||||
|
||||
@@ -21,6 +21,7 @@ class ItemMutatorPanel(wx.Panel):
|
||||
def __init__(self, parent, mod):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.stuff = mod
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
@@ -72,6 +73,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
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.timer = None
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ class ItemView(Display):
|
||||
# Get position of market group
|
||||
metagrpid = sMkt.getMetaGroupIdByItem(item)
|
||||
metatab = self.metaMap.get(metagrpid)
|
||||
metalvl = self.metalvls.get(item.ID, 0)
|
||||
metalvl = item.metaLevel or 0
|
||||
|
||||
return catname, mktgrpid, parentname, metatab, metalvl, item.name
|
||||
|
||||
@@ -237,11 +237,6 @@ class ItemView(Display):
|
||||
|
||||
def populate(self, items):
|
||||
if len(items) > 0:
|
||||
# Get dictionary with meta level attribute
|
||||
sAttr = Attribute.getInstance()
|
||||
attrs = sAttr.getAttributeInfo("metaLevel")
|
||||
sMkt = self.sMkt
|
||||
self.metalvls = sMkt.directAttrRequest(items, attrs)
|
||||
# Clear selection
|
||||
self.unselectAll()
|
||||
# Perform sorting, using item's meta levels besides other stuff
|
||||
@@ -254,11 +249,6 @@ class ItemView(Display):
|
||||
|
||||
def refresh(self, items):
|
||||
if len(items) > 1:
|
||||
# Get dictionary with meta level attribute
|
||||
sAttr = Attribute.getInstance()
|
||||
attrs = sAttr.getAttributeInfo("metaLevel")
|
||||
sMkt = self.sMkt
|
||||
self.metalvls = sMkt.directAttrRequest(items, attrs)
|
||||
# Re-sort stuff
|
||||
if self.marketBrowser.mode != 'recent':
|
||||
items.sort(key=self.itemSort)
|
||||
|
||||
@@ -19,8 +19,7 @@ class PFContextMenuPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY,
|
||||
"Disabling context menus can improve responsiveness.",
|
||||
|
||||
@@ -19,7 +19,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)",
|
||||
wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
@@ -35,7 +35,7 @@ class PFFittingEnginePref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -24,8 +24,7 @@ class PFEsiPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -1,496 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.preferenceView import PreferenceView
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Class PFGaugePref
|
||||
###########################################################################
|
||||
|
||||
|
||||
class PFGaugePreview(wx.Window):
|
||||
def __init__(self, parent, id=wx.ID_ANY, value=0, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
|
||||
wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style)
|
||||
|
||||
self.value = float(value)
|
||||
self.oldValue = self.value
|
||||
|
||||
self.percS = 0
|
||||
self.percE = 0
|
||||
|
||||
self.animate = True
|
||||
self.animDir = 1
|
||||
self._fractionDigits = 2
|
||||
|
||||
self.colorS = wx.Colour(0, 0, 0, 255)
|
||||
self.colorE = wx.Colour(0, 0, 0, 255)
|
||||
self.gradientStart = 0
|
||||
|
||||
self.bkColor = wx.Colour(0, 0, 0, 255)
|
||||
self.SetMinSize((100, -1))
|
||||
|
||||
self.font = wx.FontFromPixelSize((0, 13), wx.SWISS, wx.NORMAL, wx.NORMAL, False)
|
||||
|
||||
self.timerID = wx.NewId()
|
||||
self.timer = wx.Timer(self, self.timerID)
|
||||
self.timerInterval = 20
|
||||
|
||||
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
||||
self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)
|
||||
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
|
||||
self.Bind(wx.EVT_TIMER, self.OnTimer)
|
||||
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk)
|
||||
self.SetBackgroundStyle(wx.BG_STYLE_PAINT)
|
||||
|
||||
def OnEraseBk(self, event):
|
||||
pass
|
||||
|
||||
def OnTimer(self, event):
|
||||
if event.GetId() == self.timerID:
|
||||
self.value += self.animDir
|
||||
if self.value > 100:
|
||||
self.value = 100
|
||||
self.animDir = -1
|
||||
if self.value < 0:
|
||||
self.value = 0
|
||||
self.animDir = 1
|
||||
self.Refresh()
|
||||
|
||||
def OnWindowEnter(self, event):
|
||||
if not self.animate:
|
||||
return
|
||||
self.oldValue = self.value
|
||||
if self.timer.IsRunning():
|
||||
self.timer.Stop()
|
||||
self.timer.Start(self.timerInterval)
|
||||
event.Skip()
|
||||
|
||||
def OnWindowLeave(self, event):
|
||||
if not self.animate:
|
||||
return
|
||||
if self.timer.IsRunning():
|
||||
self.timer.Stop()
|
||||
self.value = self.oldValue
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
def CanAnimate(self, anim=True):
|
||||
self.animate = anim
|
||||
if self.timer.IsRunning():
|
||||
self.timer.Stop()
|
||||
self.value = self.oldValue
|
||||
self.Refresh()
|
||||
|
||||
def SetGradientStart(self, value):
|
||||
self.gradientStart = value
|
||||
self.Refresh()
|
||||
|
||||
def SetColour(self, colorS, colorE):
|
||||
self.colorS = colorS
|
||||
self.colorE = colorE
|
||||
|
||||
self.Refresh()
|
||||
|
||||
def SetValue(self, value):
|
||||
self.value = min(max(value, 0), 100)
|
||||
self.Refresh()
|
||||
|
||||
def SetPercentages(self, start, end):
|
||||
self.percS = start
|
||||
self.percE = end
|
||||
self.Refresh()
|
||||
|
||||
def OnPaint(self, event):
|
||||
pass
|
||||
# rect = self.GetClientRect()
|
||||
# dc = wx.AutoBufferedPaintDC(self)
|
||||
# dc.SetBackground(wx.Brush(self.bkColor))
|
||||
# dc.Clear()
|
||||
#
|
||||
# value = float(self.value)
|
||||
# if self.percS >= 100:
|
||||
# w = rect.width
|
||||
# else:
|
||||
# w = rect.width * (float(value) / 100)
|
||||
# r = copy.copy(rect)
|
||||
# r.width = w
|
||||
#
|
||||
# color = CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100)
|
||||
# if self.gradientStart > 0:
|
||||
# gcolor = color.BrightenColor(color, float(self.gradientStart) / 100)
|
||||
# gMid = color.BrightenColor(color, float(self.gradientStart / 2) / 100)
|
||||
# else:
|
||||
# gcolor = color.DarkenColor(color, float(-self.gradientStart) / 100)
|
||||
# gMid = color.DarkenColor(color, float(-self.gradientStart / 2) / 100)
|
||||
#
|
||||
# gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor)
|
||||
# dc.DrawBitmap(gBmp, 0, 0)
|
||||
# dc.SetFont(self.font)
|
||||
#
|
||||
# r = copy.copy(rect)
|
||||
# r.left += 1
|
||||
# r.top += 1
|
||||
#
|
||||
# formatStr = "{0:." + str(self._fractionDigits) + "f}%"
|
||||
# value = (self.percE - self.percS) * value / (self.percE - self.percS)
|
||||
# value = self.percS + (self.percE - self.percS) * value / 100
|
||||
#
|
||||
# dc.SetTextForeground(wx.Colour(80, 80, 80))
|
||||
# dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER)
|
||||
#
|
||||
# dc.SetTextForeground(wx.Colour(255, 255, 255))
|
||||
# dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER)
|
||||
|
||||
|
||||
class PFGaugePref(PreferenceView):
|
||||
title = "Pyfa Gauge Theme"
|
||||
|
||||
def populatePanel(self, panel):
|
||||
|
||||
self.InitDefaultColours()
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
gSizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.st0100 = wx.StaticText(panel, wx.ID_ANY, "0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT)
|
||||
self.st0100.Wrap(-1)
|
||||
gSizer1.Add(self.st0100, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp0100S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer1.Add(self.cp0100S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp0100E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer1.Add(self.cp0100E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.gauge0100S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer1.Add(self.gauge0100S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge0100M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer1.Add(self.gauge0100M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge0100E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer1.Add(self.gauge0100E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5)
|
||||
|
||||
mainSizer.Add(gSizer1, 0, wx.EXPAND, 5)
|
||||
|
||||
gSizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.st100101 = wx.StaticText(panel, wx.ID_ANY, "100 - 101", wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.ALIGN_RIGHT)
|
||||
self.st100101.Wrap(-1)
|
||||
gSizer2.Add(self.st100101, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp100101S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer2.Add(self.cp100101S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp100101E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer2.Add(self.cp100101E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.gauge100101S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer2.Add(self.gauge100101S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge100101M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer2.Add(self.gauge100101M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge100101E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer2.Add(self.gauge100101E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5)
|
||||
|
||||
mainSizer.Add(gSizer2, 0, wx.EXPAND, 5)
|
||||
|
||||
gSizer3 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.st101103 = wx.StaticText(panel, wx.ID_ANY, "101 - 103", wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.ALIGN_RIGHT)
|
||||
self.st101103.Wrap(-1)
|
||||
gSizer3.Add(self.st101103, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp101103S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer3.Add(self.cp101103S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp101103E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer3.Add(self.cp101103E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.gauge101103S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer3.Add(self.gauge101103S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge101103M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer3.Add(self.gauge101103M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge101103E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer3.Add(self.gauge101103E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5)
|
||||
|
||||
mainSizer.Add(gSizer3, 0, wx.EXPAND, 5)
|
||||
|
||||
gSizer4 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.st103105 = wx.StaticText(panel, wx.ID_ANY, "103 - 105", wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.ALIGN_RIGHT)
|
||||
self.st103105.Wrap(-1)
|
||||
gSizer4.Add(self.st103105, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp103105S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer4.Add(self.cp103105S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.cp103105E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL)
|
||||
gSizer4.Add(self.cp103105E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.gauge103105S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer4.Add(self.gauge103105S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge103105M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer4.Add(self.gauge103105M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
|
||||
|
||||
self.gauge103105E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER)
|
||||
gSizer4.Add(self.gauge103105E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5)
|
||||
|
||||
mainSizer.Add(gSizer4, 0, wx.EXPAND, 5)
|
||||
|
||||
footerSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.sl1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
footerSizer.Add(self.sl1, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
previewSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.wndPreview0100 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
previewSizer.Add(self.wndPreview0100, 1, wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.wndPreview100101 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
previewSizer.Add(self.wndPreview100101, 1, wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.wndPreview101103 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
previewSizer.Add(self.wndPreview101103, 1, wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.wndPreview103105 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
previewSizer.Add(self.wndPreview103105, 1, wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
footerSizer.Add(previewSizer, 1, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
buttonsSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.cbLink = wx.CheckBox(panel, wx.ID_ANY, "Link Colors", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
buttonsSizer.Add(self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5)
|
||||
|
||||
self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition,
|
||||
(127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS)
|
||||
buttonsSizer.Add(self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.btnRestore = wx.Button(panel, wx.ID_ANY, "Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
buttonsSizer.Add(self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.btnDump = wx.Button(panel, wx.ID_ANY, "Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
buttonsSizer.Add(self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.btnOk = wx.Button(panel, wx.ID_ANY, "Apply", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
buttonsSizer.Add(self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
footerSizer.Add(buttonsSizer, 1, wx.ALIGN_RIGHT, 5)
|
||||
mainSizer.Add(footerSizer, 0, wx.EXPAND, 5)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
|
||||
self.SetColours()
|
||||
|
||||
# self.Fit()
|
||||
# self.Layout()
|
||||
|
||||
self.sliderGradientStart.Bind(wx.EVT_SCROLL, self.OnGradientStartScroll)
|
||||
self.btnRestore.Bind(wx.EVT_BUTTON, self.RestoreDefaults)
|
||||
self.btnDump.Bind(wx.EVT_BUTTON, self.DumpColours)
|
||||
self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
|
||||
|
||||
self.cp0100S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
self.cp0100E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
|
||||
self.cp100101S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
self.cp100101E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
|
||||
self.cp101103S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
self.cp101103E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
|
||||
self.cp103105S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
self.cp103105E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged)
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("pref-gauges_big", "gui")
|
||||
|
||||
def InitDefaultColours(self):
|
||||
self.c0100S = wx.Colour(191, 191, 191, 255)
|
||||
self.c0100E = wx.Colour(96, 191, 0, 255)
|
||||
|
||||
self.c100101S = wx.Colour(191, 167, 96, 255)
|
||||
self.c100101E = wx.Colour(255, 191, 0, 255)
|
||||
|
||||
self.c101103S = wx.Colour(255, 191, 0, 255)
|
||||
self.c101103E = wx.Colour(255, 128, 0, 255)
|
||||
|
||||
self.c103105S = wx.Colour(255, 128, 0, 255)
|
||||
self.c103105E = wx.Colour(255, 0, 0, 255)
|
||||
self.gradientStart = -35
|
||||
|
||||
def SetColours(self):
|
||||
self.cp0100S.SetColour(self.c0100S)
|
||||
self.cp0100E.SetColour(self.c0100E)
|
||||
self.gauge0100S.SetColour(self.c0100S, self.c0100E)
|
||||
self.gauge0100M.SetColour(self.c0100S, self.c0100E)
|
||||
self.gauge0100E.SetColour(self.c0100S, self.c0100E)
|
||||
|
||||
self.gauge0100S.SetPercentages(0, 99.99)
|
||||
self.gauge0100M.SetPercentages(0, 99.99)
|
||||
self.gauge0100E.SetPercentages(0, 99.99)
|
||||
|
||||
self.gauge0100S.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge0100M.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge0100E.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.cp100101S.SetColour(self.c100101S)
|
||||
self.cp100101E.SetColour(self.c100101E)
|
||||
self.gauge100101S.SetColour(self.c100101S, self.c100101E)
|
||||
self.gauge100101M.SetColour(self.c100101S, self.c100101E)
|
||||
self.gauge100101E.SetColour(self.c100101S, self.c100101E)
|
||||
|
||||
self.gauge100101S.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge100101M.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge100101E.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.gauge100101S.SetPercentages(100, 100.99)
|
||||
self.gauge100101M.SetPercentages(100, 100.99)
|
||||
self.gauge100101E.SetPercentages(100, 100.99)
|
||||
|
||||
self.gauge100101S.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge100101M.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge100101E.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.cp101103S.SetColour(self.c101103S)
|
||||
self.cp101103E.SetColour(self.c101103E)
|
||||
self.gauge101103S.SetColour(self.c101103S, self.c101103E)
|
||||
self.gauge101103M.SetColour(self.c101103S, self.c101103E)
|
||||
self.gauge101103E.SetColour(self.c101103S, self.c101103E)
|
||||
|
||||
self.gauge101103S.SetPercentages(101, 102.99)
|
||||
self.gauge101103M.SetPercentages(101, 102.99)
|
||||
self.gauge101103E.SetPercentages(101, 102.99)
|
||||
|
||||
self.gauge101103S.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge101103M.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge101103E.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.cp103105S.SetColour(self.c103105S)
|
||||
self.cp103105E.SetColour(self.c103105E)
|
||||
self.gauge103105S.SetColour(self.c103105S, self.c103105E)
|
||||
self.gauge103105M.SetColour(self.c103105S, self.c103105E)
|
||||
self.gauge103105E.SetColour(self.c103105S, self.c103105E)
|
||||
|
||||
self.gauge103105S.SetPercentages(103, 104.99)
|
||||
self.gauge103105M.SetPercentages(103, 104.99)
|
||||
self.gauge103105E.SetPercentages(103, 104.99)
|
||||
|
||||
self.gauge103105S.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge103105M.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
self.gauge103105E.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.wndPreview0100.SetColour(self.c0100S, self.c0100E)
|
||||
self.wndPreview0100.SetPercentages(0, 99.99)
|
||||
self.wndPreview0100.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.wndPreview100101.SetColour(self.c100101S, self.c100101E)
|
||||
self.wndPreview100101.SetPercentages(100, 100.99)
|
||||
self.wndPreview100101.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.wndPreview101103.SetColour(self.c101103S, self.c101103E)
|
||||
self.wndPreview101103.SetPercentages(101, 102.99)
|
||||
self.wndPreview101103.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
self.wndPreview103105.SetColour(self.c103105S, self.c103105E)
|
||||
self.wndPreview103105.SetPercentages(103, 104.99)
|
||||
self.wndPreview103105.SetGradientStart(self.sliderGradientStart.GetValue())
|
||||
|
||||
def OnGradientStartScroll(self, event):
|
||||
self.gradientStart = self.sliderGradientStart.GetValue()
|
||||
self.SetColours()
|
||||
event.Skip()
|
||||
|
||||
@staticmethod
|
||||
def OnOk(event):
|
||||
# Apply New Settings
|
||||
event.Skip()
|
||||
|
||||
def DumpColours(self, event):
|
||||
print(("Gradient start: %d" % self.sliderGradientStart.GetValue()))
|
||||
print((" 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E))
|
||||
print(("100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E))
|
||||
print(("101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E))
|
||||
print(("103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E))
|
||||
|
||||
event.Skip()
|
||||
|
||||
def RestoreDefaults(self, event):
|
||||
self.InitDefaultColours()
|
||||
self.sliderGradientStart.SetValue(self.gradientStart)
|
||||
self.SetColours()
|
||||
|
||||
event.Skip()
|
||||
|
||||
def OnColourChanged(self, event):
|
||||
|
||||
color = event.EventObject.GetColour()
|
||||
cpObj = event.EventObject
|
||||
|
||||
if cpObj == self.cp0100S:
|
||||
self.c0100S = color
|
||||
|
||||
if cpObj == self.cp0100E:
|
||||
self.c0100E = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c100101S = color
|
||||
|
||||
if cpObj == self.cp100101S:
|
||||
self.c100101S = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c0100E = color
|
||||
|
||||
if cpObj == self.cp100101E:
|
||||
self.c100101E = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c101103S = color
|
||||
|
||||
if cpObj == self.cp101103S:
|
||||
self.c101103S = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c100101E = color
|
||||
|
||||
if cpObj == self.cp101103E:
|
||||
self.c101103E = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c103105S = color
|
||||
|
||||
if cpObj == self.cp103105S:
|
||||
self.c103105S = color
|
||||
if self.cbLink.IsChecked():
|
||||
self.c101103E = color
|
||||
|
||||
if cpObj == self.cp103105E:
|
||||
self.c103105E = color
|
||||
|
||||
self.SetColours()
|
||||
event.Skip()
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
|
||||
PFGaugePref.register()
|
||||
@@ -24,9 +24,9 @@ class PFGeneralPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
helpCursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW)
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
@@ -86,6 +86,10 @@ class PFGeneralPref(PreferenceView):
|
||||
'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.rbAddLabels = wx.RadioBox(panel, -1, "Extra info in Additions panel tab names", wx.DefaultPosition, wx.DefaultSize, ["None", "Quantity of active items", "Quantity of all items"], 1, wx.RA_SPECIFY_COLS)
|
||||
mainSizer.Add(self.rbAddLabels, 0, wx.EXPAND | wx.TOP | wx.RIGHT | wx.BOTTOM, 10)
|
||||
self.rbAddLabels.Bind(wx.EVT_RADIOBOX, self.OnAddLabelsChange)
|
||||
|
||||
self.sFit = Fit.getInstance()
|
||||
|
||||
self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"])
|
||||
@@ -101,6 +105,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.rbAddLabels.SetSelection(self.sFit.serviceFittingOptions["additionsLabels"])
|
||||
|
||||
self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange)
|
||||
self.cbDefaultCharImplants.Bind(wx.EVT_CHECKBOX, self.OnCBDefaultCharImplantsStateChange)
|
||||
@@ -187,6 +192,13 @@ class PFGeneralPref(PreferenceView):
|
||||
def onCBReloadAll(self, event):
|
||||
self.sFit.serviceFittingOptions["ammoChangeAll"] = self.cbReloadAll.GetValue()
|
||||
|
||||
def OnAddLabelsChange(self, event):
|
||||
self.sFit.serviceFittingOptions["additionsLabels"] = event.GetInt()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
self.sFit.refreshFit(fitID)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitIDs=(fitID,)))
|
||||
event.Skip()
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("prefs_settings", "gui")
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class PFHTMLExportPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -23,7 +23,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)",
|
||||
wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
@@ -28,8 +28,7 @@ class PFMarketPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, "Market && Prices", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -24,8 +24,7 @@ class PFNetworkPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -23,8 +23,7 @@ class PFStatViewPref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY,
|
||||
"Changes require restart of pyfa to take effect.",
|
||||
|
||||
@@ -24,7 +24,7 @@ class PFUpdatePref(PreferenceView):
|
||||
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stTitle.Wrap(-1)
|
||||
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
|
||||
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
|
||||
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
@@ -283,9 +283,7 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
event.Skip()
|
||||
|
||||
def editCheckEsc(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.RestoreEditButton()
|
||||
else:
|
||||
event.Skip()
|
||||
@@ -344,13 +342,12 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
if mstate.GetModifiers() == wx.MOD_SHIFT or mstate.MiddleIsDown():
|
||||
self.deleteFit()
|
||||
else:
|
||||
dlg = wx.MessageDialog(
|
||||
self, "Do you really want to delete this fit?", "Confirm Delete",
|
||||
wx.YES | wx.NO | wx.ICON_QUESTION)
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
self.deleteFit()
|
||||
else:
|
||||
dlg.Destroy()
|
||||
with wx.MessageDialog(
|
||||
self.GetTopLevelParent(), "Do you really want to delete this fit?", "Confirm Delete",
|
||||
wx.YES | wx.NO | wx.ICON_QUESTION
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
self.deleteFit()
|
||||
|
||||
def deleteFit(self, event=None):
|
||||
pyfalog.debug("Deleting ship fit.")
|
||||
|
||||
@@ -109,10 +109,10 @@ class NavigationPanel(SFItem.SFBrowserItem):
|
||||
|
||||
def OnBrowserSearchBoxKeyPress(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.BrowserSearchBox.Show(False)
|
||||
elif event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
elif keycode == wx.WXK_BACK and modifiers == wx.MOD_CONTROL:
|
||||
HandleCtrlBackspace(self.BrowserSearchBox)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
@@ -176,9 +176,7 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
self.Refresh()
|
||||
|
||||
def editCheckEsc(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.tcFitName.Show(False)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
@@ -36,7 +36,6 @@ class RechargeViewFull(StatsView):
|
||||
self.parent = parent
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.mainFrame.Bind(GE.EFFECTIVE_HP_TOGGLED, self.toggleEffective)
|
||||
self.effective = True
|
||||
|
||||
def getHeaderText(self, fit):
|
||||
return "Recharge rates"
|
||||
@@ -45,9 +44,15 @@ class RechargeViewFull(StatsView):
|
||||
width, height = self.parent.GetTextExtent(text)
|
||||
return width
|
||||
|
||||
@property
|
||||
def effective(self):
|
||||
try:
|
||||
return self.parent.nameViewMap['resistancesViewFull'].showEffective
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
def toggleEffective(self, event):
|
||||
event.Skip()
|
||||
self.effective = event.effective
|
||||
sFit = Fit.getInstance()
|
||||
self.refreshPanel(sFit.getFit(self.mainFrame.getActiveFit()))
|
||||
|
||||
@@ -104,8 +109,7 @@ class RechargeViewFull(StatsView):
|
||||
|
||||
def refreshPanel(self, fit):
|
||||
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
|
||||
|
||||
unit = " EHP/s" if self.parent.nameViewMap['resistancesViewFull'].showEffective else " HP/s"
|
||||
unit = " EHP/s" if self.effective else " HP/s"
|
||||
|
||||
for stability in ("reinforced", "sustained"):
|
||||
if stability == "reinforced" and fit is not None:
|
||||
|
||||
@@ -100,6 +100,10 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
def refreshPanel(self, fit):
|
||||
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
|
||||
|
||||
sensorValues = {
|
||||
"main": lambda: fit.scanStrength,
|
||||
"jamChance": lambda: fit.jamChance}
|
||||
|
||||
cargoNamesOrder = OrderedDict((
|
||||
("fleetHangarCapacity", "Fleet hangar"),
|
||||
("shipMaintenanceBayCapacity", "Maintenance bay"),
|
||||
@@ -117,8 +121,7 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
("specialSalvageHoldCapacity", "Salvage hold"),
|
||||
("specialCommandCenterHoldCapacity", "Command center hold"),
|
||||
("specialPlanetaryCommoditiesHoldCapacity", "Planetary goods hold"),
|
||||
("specialQuafeHoldCapacity", "Quafe hold")
|
||||
))
|
||||
("specialQuafeHoldCapacity", "Quafe hold")))
|
||||
|
||||
cargoValues = {
|
||||
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
|
||||
@@ -138,13 +141,12 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
|
||||
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"),
|
||||
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"),
|
||||
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
|
||||
}
|
||||
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")}
|
||||
|
||||
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),
|
||||
("labelRange", {"main": lambda: fit.maxTargetRange / 1000}, 3, 0, 0, "km"),
|
||||
("labelScanRes", {"main": lambda: fit.ship.getModifiedItemAttr("scanResolution")}, 3, 0, 0, "mm"),
|
||||
("labelSensorStr", {"main": lambda: fit.scanStrength}, 3, 0, 0, ""),
|
||||
("labelSensorStr", sensorValues, 3, 0, 0, ""),
|
||||
("labelCtrlRange", {"main": lambda: fit.extraAttributes["droneControlRange"] / 1000}, 3, 0, 0, "km"),
|
||||
("labelFullSpeed", {"main": lambda: fit.maxSpeed}, 3, 0, 0, "m/s"),
|
||||
("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"),
|
||||
@@ -176,6 +178,15 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
unit))
|
||||
else:
|
||||
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
|
||||
elif labelName == "labelSensorStr":
|
||||
ecmChance = otherValues["jamChance"]
|
||||
ecmChance = round(ecmChance, 1)
|
||||
if ecmChance:
|
||||
label.SetLabel("{} ({}%)".format(
|
||||
formatAmount(mainValue, prec, lowest, highest),
|
||||
formatAmount(ecmChance, 3, 0, 0)))
|
||||
else:
|
||||
label.SetLabel("{}".format(formatAmount(mainValue, prec, lowest, highest)))
|
||||
else:
|
||||
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
|
||||
# Tooltip stuff
|
||||
@@ -195,10 +206,14 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
warpScrambleStatus = "Warp Core Strength: %.1f" % 0
|
||||
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
|
||||
elif labelName == "labelSensorStr":
|
||||
if fit.jamChance > 0:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
|
||||
ecmChance = otherValues["jamChance"]
|
||||
ecmChance = round(ecmChance, 1)
|
||||
if ecmChance > 0:
|
||||
label.SetToolTip(wx.ToolTip("Type: {}\n{}% chance to be jammed".format(
|
||||
fit.scanType,
|
||||
formatAmount(ecmChance, 3, 0, 0))))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
|
||||
label.SetToolTip(wx.ToolTip("Type: {}".format(fit.scanType)))
|
||||
elif labelName == "labelFullAlignTime":
|
||||
alignTime = "Align:\t%.3fs" % mainValue
|
||||
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
|
||||
@@ -225,14 +240,6 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
label.SetToolTip(wx.ToolTip("%s\n%s" % (maxWarpDistance, warpScrambleStatus)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip(""))
|
||||
elif labelName == "labelSensorStr":
|
||||
if fit:
|
||||
if fit.jamChance > 0:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
|
||||
else:
|
||||
label.SetToolTip(wx.ToolTip(""))
|
||||
elif labelName == "labelFullCargo":
|
||||
if fit:
|
||||
cachedCargo = self._cachedValues[counter]
|
||||
|
||||
@@ -612,7 +612,10 @@ class Miscellanea(ViewColumn):
|
||||
fit = Fit.getInstance().getFit(self.fittingView.getActiveFit())
|
||||
ehpTotal = fit.ehp
|
||||
hpTotal = fit.hp
|
||||
useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
|
||||
try:
|
||||
useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
|
||||
except KeyError:
|
||||
useEhp = False
|
||||
tooltip = "{0} restored over duration using charges (plus reload)".format(boosted_attribute)
|
||||
|
||||
if useEhp and boosted_attribute == "HP" and "Remote" not in itemGroup:
|
||||
@@ -686,9 +689,8 @@ class Miscellanea(ViewColumn):
|
||||
baseRange = stuff.getModifiedChargeAttr("baseScanRange")
|
||||
if not scanStr or not baseRange:
|
||||
return "", None
|
||||
strTwoAu = scanStr / (2.0 / baseRange)
|
||||
text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3))
|
||||
tooltip = "Scan strength with 2 AU scan range"
|
||||
text = "{}".format(formatAmount(scanStr, 4, 0, 3))
|
||||
tooltip = "Scan strength at {} AU scan range".format(formatAmount(baseRange, 3, 0, 0))
|
||||
return text, tooltip
|
||||
else:
|
||||
return "", None
|
||||
|
||||
61
gui/builtinViewColumns/projectionRange.py
Normal file
61
gui/builtinViewColumns/projectionRange.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# coding: utf-8
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.fit import Fit
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
from gui.viewColumn import ViewColumn
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class ProjectionRangeColumn(ViewColumn):
|
||||
|
||||
name = 'Projection Range'
|
||||
|
||||
def __init__(self, fittingView, params):
|
||||
super().__init__(fittingView)
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.imageId = fittingView.imageList.GetImageIndex(1391, "icons")
|
||||
self.bitmap = BitmapLoader.getBitmap(1391, "icons")
|
||||
self.mask = wx.LIST_MASK_IMAGE
|
||||
|
||||
def getText(self, stuff):
|
||||
if isinstance(stuff, Fit):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
info = stuff.getProjectionInfo(fitID)
|
||||
projRange = info.projectionRange
|
||||
else:
|
||||
projRange = getattr(stuff, 'projectionRange', None)
|
||||
if projRange is None:
|
||||
return ''
|
||||
return formatAmount(projRange, 3, 0, 3, unitName='m')
|
||||
|
||||
def getToolTip(self, mod):
|
||||
return 'Projection Range'
|
||||
|
||||
|
||||
ProjectionRangeColumn.register()
|
||||
@@ -280,12 +280,12 @@ class FittingView(d.Display):
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
modifiers = event.GetModifiers()
|
||||
if keycode == wx.WXK_ESCAPE and modifiers == wx.MOD_NONE:
|
||||
self.unselectAll()
|
||||
elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
elif keycode == 65 and modifiers == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and modifiers == wx.MOD_NONE:
|
||||
modules = [m for m in self.getSelectedMods() if not m.isEmpty]
|
||||
self.removeModule(modules)
|
||||
event.Skip()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import re
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -10,6 +12,12 @@ from gui.marketBrowser import SearchBox
|
||||
from service.market import Market
|
||||
|
||||
|
||||
def stripHtml(text):
|
||||
text = re.sub('<\s*br\s*/?\s*>', '\n', text)
|
||||
text = re.sub('</?[^/]+?(/\s*)?>', '', text)
|
||||
return text
|
||||
|
||||
|
||||
class BaseImplantEditorView(wx.Panel):
|
||||
|
||||
def addMarketViewImage(self, iconFile):
|
||||
@@ -68,8 +76,10 @@ class BaseImplantEditorView(wx.Panel):
|
||||
|
||||
self.SetSizer(pmainSizer)
|
||||
|
||||
# Populate the market tree
|
||||
self.hoveredLeftTreeTypeID = None
|
||||
self.hoveredRightListRow = None
|
||||
|
||||
# Populate the market tree
|
||||
sMkt = Market.getInstance()
|
||||
for mktGrp in sMkt.getImplantTree():
|
||||
iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp))
|
||||
@@ -82,9 +92,13 @@ class BaseImplantEditorView(wx.Panel):
|
||||
# Bind the event to replace dummies by real data
|
||||
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.itemSelected)
|
||||
self.availableImplantsTree.Bind(wx.EVT_MOTION, self.OnLeftTreeMouseMove)
|
||||
self.availableImplantsTree.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeftTreeMouseLeave)
|
||||
|
||||
self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemSelected)
|
||||
|
||||
self.pluggedImplantsTree.Bind(wx.EVT_MOTION, self.OnRightListMouseMove)
|
||||
|
||||
# Bind add & remove buttons
|
||||
self.btnAdd.Bind(wx.EVT_BUTTON, self.itemSelected)
|
||||
self.btnRemove.Bind(wx.EVT_BUTTON, self.removeItem)
|
||||
@@ -193,6 +207,55 @@ class BaseImplantEditorView(wx.Panel):
|
||||
self.removeImplantFromContext(self.implants[pos])
|
||||
self.update()
|
||||
|
||||
# Due to https://github.com/wxWidgets/Phoenix/issues/1372 we cannot set tooltips on
|
||||
# tree itself; work this around with following two methods, by setting tooltip to
|
||||
# parent window
|
||||
def OnLeftTreeMouseMove(self, event):
|
||||
event.Skip()
|
||||
treeItemId, _ = self.availableImplantsTree.HitTest(event.Position)
|
||||
if not treeItemId:
|
||||
if self.hoveredLeftTreeTypeID is not None:
|
||||
self.hoveredLeftTreeTypeID = None
|
||||
self.SetToolTip(None)
|
||||
return
|
||||
item = self.availableImplantsTree.GetItemData(treeItemId)
|
||||
isImplant = getattr(item, 'isImplant', False)
|
||||
if not isImplant:
|
||||
if self.hoveredLeftTreeTypeID is not None:
|
||||
self.hoveredLeftTreeTypeID = None
|
||||
self.SetToolTip(None)
|
||||
return
|
||||
if self.hoveredLeftTreeTypeID == item.ID:
|
||||
return
|
||||
if self.ToolTip is not None:
|
||||
self.SetToolTip(None)
|
||||
else:
|
||||
self.hoveredLeftTreeTypeID = item.ID
|
||||
toolTip = wx.ToolTip(stripHtml(item.description))
|
||||
toolTip.SetMaxWidth(self.GetSize().Width)
|
||||
self.SetToolTip(toolTip)
|
||||
|
||||
def OnLeftTreeMouseLeave(self, event):
|
||||
event.Skip()
|
||||
self.SetToolTip(None)
|
||||
|
||||
def OnRightListMouseMove(self, event):
|
||||
event.Skip()
|
||||
row, _, col = self.pluggedImplantsTree.HitTestSubItem(event.Position)
|
||||
if row != self.hoveredRightListRow:
|
||||
if self.pluggedImplantsTree.ToolTip is not None:
|
||||
self.pluggedImplantsTree.SetToolTip(None)
|
||||
else:
|
||||
self.hoveredRightListRow = row
|
||||
try:
|
||||
implant = self.implants[row]
|
||||
except IndexError:
|
||||
self.pluggedImplantsTree.SetToolTip(None)
|
||||
else:
|
||||
toolTip = wx.ToolTip(stripHtml(implant.item.description))
|
||||
toolTip.SetMaxWidth(self.pluggedImplantsTree.GetSize().Width)
|
||||
self.pluggedImplantsTree.SetToolTip(toolTip)
|
||||
|
||||
|
||||
class AvailableImplantsView(d.Display):
|
||||
DEFAULT_COLS = ["attr:implantness",
|
||||
@@ -212,6 +275,7 @@ class ItemView(d.Display):
|
||||
self.parent = parent
|
||||
self.searchBox = parent.searchBox
|
||||
|
||||
self.hoveredRow = None
|
||||
self.items = []
|
||||
|
||||
# Bind search actions
|
||||
@@ -220,6 +284,8 @@ class ItemView(d.Display):
|
||||
self.searchBox.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch)
|
||||
self.searchBox.Bind(SBox.EVT_TEXT, self.scheduleSearch)
|
||||
|
||||
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
||||
|
||||
def clearSearch(self, event=None):
|
||||
if self.IsShown():
|
||||
self.parent.availableImplantsTree.Show()
|
||||
@@ -255,3 +321,20 @@ class ItemView(d.Display):
|
||||
self.items = sorted(list(items), key=lambda i: i.name)
|
||||
|
||||
self.update(self.items)
|
||||
|
||||
def OnMouseMove(self, event):
|
||||
event.Skip()
|
||||
row, _, col = self.HitTestSubItem(event.Position)
|
||||
if row != self.hoveredRow:
|
||||
if self.ToolTip is not None:
|
||||
self.SetToolTip(None)
|
||||
else:
|
||||
self.hoveredRow = row
|
||||
try:
|
||||
item = self.items[row]
|
||||
except IndexError:
|
||||
self.SetToolTip(None)
|
||||
else:
|
||||
toolTip = wx.ToolTip(stripHtml(item.description))
|
||||
toolTip.SetMaxWidth(self.GetSize().Width)
|
||||
self.SetToolTip(toolTip)
|
||||
|
||||
@@ -155,8 +155,8 @@ class CharacterEditor(AuxiliaryFrame):
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(
|
||||
parent, id=wx.ID_ANY, title="Character Editor", resizeable=True,
|
||||
pos=wx.DefaultPosition, size=wx.Size(640, 600))
|
||||
parent, id=wx.ID_ANY, title="Character Editor", resizeable=True, pos=wx.DefaultPosition,
|
||||
size=wx.Size(950, 650) if "wxGTK" in wx.PlatformInfo else wx.Size(850, 600))
|
||||
|
||||
i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui"))
|
||||
self.SetIcon(i)
|
||||
@@ -209,6 +209,7 @@ class CharacterEditor(AuxiliaryFrame):
|
||||
self.SetSizer(mainSizer)
|
||||
self.Layout()
|
||||
|
||||
self.SetMinSize(self.GetSize())
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
@@ -257,9 +258,7 @@ class CharacterEditor(AuxiliaryFrame):
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -13,14 +13,17 @@
|
||||
#
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
import math
|
||||
from functools import lru_cache
|
||||
|
||||
import wx
|
||||
import wx.lib.newevent
|
||||
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils import draw
|
||||
from gui.utils import color as color_utils
|
||||
from gui.utils import color as color_utils, draw, fonts
|
||||
from service.fit import Fit
|
||||
from gui.utils import fonts
|
||||
|
||||
|
||||
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
|
||||
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
|
||||
@@ -89,11 +92,15 @@ class PageAdding(_PageAdding, VetoAble):
|
||||
|
||||
class ChromeNotebook(wx.Panel):
|
||||
|
||||
def __init__(self, parent, can_add=True):
|
||||
def __init__(self, parent, can_add=True, tabWidthMode=0):
|
||||
"""
|
||||
Instance of Notebook. Initializes general layout, includes methods
|
||||
for setting current page, replacing pages, any public function for the
|
||||
notebook
|
||||
|
||||
width modes:
|
||||
- 0: legacy (all tabs have equal width)
|
||||
- 1: all tabs take just enough space to fit text
|
||||
"""
|
||||
super().__init__(parent, wx.ID_ANY, size=(-1, -1))
|
||||
|
||||
@@ -103,7 +110,7 @@ class ChromeNotebook(wx.Panel):
|
||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
tabs_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.tabs_container = _TabsContainer(self, can_add=can_add)
|
||||
self.tabs_container = _TabsContainer(self, can_add=can_add, tabWidthMode=tabWidthMode)
|
||||
tabs_sizer.Add(self.tabs_container, 0, wx.EXPAND)
|
||||
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
@@ -296,7 +303,14 @@ class ChromeNotebook(wx.Panel):
|
||||
|
||||
def SetPageTitle(self, i, text, refresh=True):
|
||||
tab = self.tabs_container.tabs[i]
|
||||
tab.text = text
|
||||
tab.baseText = text
|
||||
if refresh:
|
||||
self.tabs_container.AdjustTabsSize()
|
||||
self.Refresh()
|
||||
|
||||
def SetPageTitleExtra(self, i, text, refresh=True):
|
||||
tab = self.tabs_container.tabs[i]
|
||||
tab.extraText = text
|
||||
if refresh:
|
||||
self.tabs_container.AdjustTabsSize()
|
||||
self.Refresh()
|
||||
@@ -354,7 +368,8 @@ class _TabRenderer:
|
||||
height = max(height, self.min_height)
|
||||
|
||||
self.disabled = False
|
||||
self.text = text
|
||||
self.baseText = text
|
||||
self.extraText = ''
|
||||
self.tab_size = (width, height)
|
||||
self.closeable = closeable
|
||||
self.selected = False
|
||||
@@ -368,6 +383,10 @@ class _TabRenderer:
|
||||
self.position = (0, 0) # Not used internally for rendering - helper for tab container
|
||||
self.InitTab()
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self.baseText + self.extraText
|
||||
|
||||
def SetPosition(self, position):
|
||||
self.position = position
|
||||
|
||||
@@ -685,15 +704,15 @@ class _AddRenderer:
|
||||
|
||||
class _TabsContainer(wx.Panel):
|
||||
def __init__(self, parent, pos=(50, 0), size=(100, 22), id=wx.ID_ANY,
|
||||
can_add=True):
|
||||
can_add=True, tabWidthMode=0):
|
||||
"""
|
||||
Defines the tab container. Handles functions such as tab selection and
|
||||
dragging, and defines minimum width of tabs (all tabs are of equal
|
||||
width, which is determined via widest tab). Also handles the tab
|
||||
preview, if any.
|
||||
"""
|
||||
|
||||
super().__init__(parent, id, pos, size)
|
||||
self.tabWidthMode = tabWidthMode
|
||||
|
||||
self.tabs = []
|
||||
self.width, self.height = size
|
||||
@@ -720,8 +739,7 @@ class _TabsContainer(wx.Panel):
|
||||
self.show_add_button = can_add
|
||||
|
||||
self.tab_container_width = self.width - self.reserved
|
||||
self.tab_min_width = self.width
|
||||
self.tab_shadow = _TabRenderer((self.tab_min_width, self.height + 1))
|
||||
self.fxBmps = {}
|
||||
|
||||
self.add_button = _AddRenderer()
|
||||
self.add_bitmap = self.add_button.Render()
|
||||
@@ -1173,7 +1191,7 @@ class _TabsContainer(wx.Panel):
|
||||
|
||||
if not tab.IsSelected():
|
||||
# drop shadow first
|
||||
mdc.DrawBitmap(self.fx_bmp, posx, posy, True)
|
||||
mdc.DrawBitmap(self.fxBmps[tab], posx, posy, True)
|
||||
bmp = tab.Render()
|
||||
img = bmp.ConvertToImage()
|
||||
img = img.AdjustChannels(1, 1, 1, 0.85)
|
||||
@@ -1191,7 +1209,7 @@ class _TabsContainer(wx.Panel):
|
||||
if selected:
|
||||
posx, posy = selected.GetPosition()
|
||||
# drop shadow first
|
||||
mdc.DrawBitmap(self.fx_bmp, posx, posy, True)
|
||||
mdc.DrawBitmap(self.fxBmps[selected], posx, posy, True)
|
||||
|
||||
bmp = selected.Render()
|
||||
|
||||
@@ -1211,16 +1229,21 @@ class _TabsContainer(wx.Panel):
|
||||
|
||||
def UpdateTabFX(self):
|
||||
""" Updates tab drop shadow bitmap """
|
||||
self.tab_shadow.SetSize((self.tab_min_width, self.height + 1))
|
||||
fx_bmp = self.tab_shadow.Render()
|
||||
self.fxBmps.clear()
|
||||
for tab in self.tabs:
|
||||
tabW, tabH = tab.tab_size
|
||||
self.fxBmps[tab] = self.GetTabFx(tabW, self.height + 1)
|
||||
|
||||
@lru_cache(maxsize=50)
|
||||
def GetTabFx(self, width, height):
|
||||
renderer = _TabRenderer((width, height))
|
||||
fx_bmp = renderer.Render()
|
||||
img = fx_bmp.ConvertToImage()
|
||||
if not img.HasAlpha():
|
||||
img.InitAlpha()
|
||||
img = img.Blur(2)
|
||||
img = img.AdjustChannels(0.3, 0.3, 0.3, 0.35)
|
||||
|
||||
self.fx_bmp = wx.Bitmap(img)
|
||||
return wx.Bitmap(img)
|
||||
|
||||
def AddTab(self, title=wx.EmptyString, img=None, closeable=False):
|
||||
self.ClearTabsSelected()
|
||||
@@ -1262,29 +1285,74 @@ class _TabsContainer(wx.Panel):
|
||||
Adjust tab sizes to ensure that they are all consistent and can fit into
|
||||
the tab container.
|
||||
"""
|
||||
if self.tabWidthMode == 1:
|
||||
if self.GetTabsCount() > 0:
|
||||
availableW = self.tab_container_width
|
||||
overlapSavedW = max(0, len(self.tabs)) * self.inclination * 2
|
||||
tabsGrouped = {}
|
||||
for tab in self.tabs:
|
||||
tabW, _ = tab.GetMinSize()
|
||||
tabsGrouped.setdefault(math.ceil(tabW), []).append(tab)
|
||||
clippedTabs = []
|
||||
clipW = max(tabsGrouped, default=0)
|
||||
|
||||
# first we loop through our tabs and calculate the the largest tab. This
|
||||
# is the size that we will base our calculations off
|
||||
def getUnclippedW():
|
||||
unclippedW = 0
|
||||
for w, tabs in tabsGrouped.items():
|
||||
unclippedW += w * len(tabs)
|
||||
return unclippedW
|
||||
while tabsGrouped:
|
||||
# Check if we're within width limit
|
||||
neededW = 0
|
||||
for w, tabs in tabsGrouped.items():
|
||||
neededW += w * len(tabs)
|
||||
if clippedTabs:
|
||||
neededW += clipW * len(clippedTabs)
|
||||
if neededW <= availableW + overlapSavedW:
|
||||
break
|
||||
# If we're not, extract widest tab group and mark it for clipping
|
||||
currentTabs = tabsGrouped.pop(max(tabsGrouped))
|
||||
clippedTabs.extend(currentTabs)
|
||||
proposedClipWidth = math.floor((availableW + overlapSavedW - getUnclippedW()) / len(clippedTabs))
|
||||
if not tabsGrouped or proposedClipWidth >= max(tabsGrouped, default=0):
|
||||
clipW = max(0, proposedClipWidth)
|
||||
break
|
||||
else:
|
||||
clipW = max(tabsGrouped)
|
||||
# Assign width for unclipped tabs
|
||||
for w, tabs in tabsGrouped.items():
|
||||
for tab in tabs:
|
||||
tab.SetSize((w, self.height))
|
||||
if clippedTabs:
|
||||
# Some width remains to be used due to rounding to integer
|
||||
extraWTotal = availableW + overlapSavedW - getUnclippedW() - clipW * len(clippedTabs)
|
||||
extraWPerTab = math.ceil(extraWTotal / len(clippedTabs))
|
||||
# Assign width for clipped tabs
|
||||
for tab in clippedTabs:
|
||||
extraW = min(extraWTotal, extraWPerTab)
|
||||
extraWTotal -= extraW
|
||||
tab.SetSize((clipW + extraW, self.height))
|
||||
else:
|
||||
# first we loop through our tabs and calculate the the largest tab. This
|
||||
# is the size that we will base our calculations off
|
||||
max_width = 100 # Tab should be at least 100
|
||||
for tab in self.tabs:
|
||||
mw, _ = tab.GetMinSize() # Tab min size includes tab contents
|
||||
max_width = max(mw, max_width)
|
||||
|
||||
max_width = 100 # Tab should be at least 100
|
||||
for tab in self.tabs:
|
||||
mw, _ = tab.GetMinSize() # Tab min size includes tab contents
|
||||
max_width = max(mw, max_width)
|
||||
tabWidth = 0
|
||||
# Divide tab container by number of tabs and add inclination. This will
|
||||
# return the ideal max size for the containers size
|
||||
if self.GetTabsCount() > 0:
|
||||
dx = self.tab_container_width / self.GetTabsCount() + self.inclination * 2
|
||||
tabWidth = min(dx, max_width)
|
||||
|
||||
# Divide tab container by number of tabs and add inclination. This will
|
||||
# return the ideal max size for the containers size
|
||||
if self.GetTabsCount() > 0:
|
||||
dx = self.tab_container_width / self.GetTabsCount() + self.inclination * 2
|
||||
self.tab_min_width = min(dx, max_width)
|
||||
|
||||
# Apply new size to all tabs
|
||||
for tab in self.tabs:
|
||||
tab.SetSize((self.tab_min_width, self.height))
|
||||
|
||||
if self.GetTabsCount() > 0:
|
||||
# update drop shadow based on new sizes
|
||||
self.UpdateTabFX()
|
||||
# Apply new size to all tabs
|
||||
for tab in self.tabs:
|
||||
tab.SetSize((tabWidth, self.height))
|
||||
|
||||
# update drop shadow based on new sizes
|
||||
self.UpdateTabFX()
|
||||
self.UpdateTabsPosition()
|
||||
|
||||
def UpdateTabsPosition(self, skip_tab=None):
|
||||
|
||||
@@ -301,10 +301,13 @@ class Display(wx.ListCtrl):
|
||||
|
||||
def ensureSelection(self, clickedPos):
|
||||
"""
|
||||
On mac, when right-click on any item happens, it doesn't get selected.
|
||||
This method ensures that selection actually happens.
|
||||
On windows with Ctrl is pressed, or on Mac, when right-click on any item happens,
|
||||
the item doesn't get selected. This method ensures that only clicked item is selected.
|
||||
"""
|
||||
if 'wxMac' in wx.PlatformInfo:
|
||||
if (
|
||||
'wxMac' in wx.PlatformInfo or
|
||||
('wxMSW' in wx.PlatformInfo and wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL)
|
||||
):
|
||||
if clickedPos != -1:
|
||||
selectedPoss = self.getSelectedRows()
|
||||
if clickedPos not in selectedPoss:
|
||||
|
||||
@@ -65,9 +65,7 @@ class ErrorHandler:
|
||||
class ErrorFrame(AuxiliaryFrame):
|
||||
|
||||
def __init__(self, parent=None, error_title='Error!'):
|
||||
super().__init__(
|
||||
parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition,
|
||||
size=wx.Size(500, 600), style=wx.STAY_ON_TOP)
|
||||
super().__init__(parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600))
|
||||
|
||||
from eos.config import gamedata_version, gamedata_date
|
||||
|
||||
|
||||
@@ -92,9 +92,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
self.charChoice.SetSelection(0)
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
@@ -248,9 +246,7 @@ class ExportToEve(AuxiliaryFrame):
|
||||
self.charChoice.SetSelection(0)
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
@@ -358,9 +354,7 @@ class SsoCharacterMgmt(AuxiliaryFrame):
|
||||
event.Skip()
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -56,6 +56,7 @@ from .gui.localModule.replace import GuiReplaceLocalModuleCommand
|
||||
from .gui.localModule.swap import GuiSwapLocalModulesCommand
|
||||
from .gui.localModuleCargo.cargoToLocalModule import GuiCargoToLocalModuleCommand
|
||||
from .gui.localModuleCargo.localModuleToCargo import GuiLocalModuleToCargoCommand
|
||||
from .gui.projectedChangeProjectionRange import GuiChangeProjectedItemsProjectionRangeCommand
|
||||
from .gui.projectedChangeStates import GuiChangeProjectedItemStatesCommand
|
||||
from .gui.projectedDrone.add import GuiAddProjectedDroneCommand
|
||||
from .gui.projectedDrone.changeAmount import GuiChangeProjectedDroneAmountCommand
|
||||
|
||||
40
gui/fitCommands/calc/drone/projectedChangeProjectionRange.py
Normal file
40
gui/fitCommands/calc/drone/projectedChangeProjectionRange.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class CalcChangeProjectedDroneProjectionRangeCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, itemID, projectionRange):
|
||||
wx.Command.__init__(self, True, 'Change Projected Drone Projection Range')
|
||||
self.fitID = fitID
|
||||
self.itemID = itemID
|
||||
self.projectionRange = projectionRange
|
||||
self.savedProjectionRange = None
|
||||
|
||||
def Do(self):
|
||||
pyfalog.debug('Doing change of projected drone {} projection range to {} on fit {}'.format(
|
||||
self.itemID, self.projectionRange, self.fitID))
|
||||
fit = Fit.getInstance().getFit(self.fitID)
|
||||
drone = next((pd for pd in fit.projectedDrones if pd.itemID == self.itemID), None)
|
||||
if drone is None:
|
||||
pyfalog.warning('Cannot find projected drone')
|
||||
return False
|
||||
if drone.projectionRange == self.projectionRange:
|
||||
return False
|
||||
self.savedProjectionRange = drone.projectionRange
|
||||
drone.projectionRange = self.projectionRange
|
||||
return True
|
||||
|
||||
def Undo(self):
|
||||
pyfalog.debug('Undoing change of projected drone {} projection range to {} on fit {}'.format(
|
||||
self.itemID, self.projectionRange, self.fitID))
|
||||
cmd = CalcChangeProjectedDroneProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
itemID=self.itemID,
|
||||
projectionRange=self.savedProjectionRange)
|
||||
return cmd.Do()
|
||||
@@ -0,0 +1,37 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class CalcChangeProjectedFighterProjectionRangeCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position, projectionRange):
|
||||
wx.Command.__init__(self, True, 'Change Projected Fighter Projection Range')
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
self.projectionRange = projectionRange
|
||||
self.savedProjectionRange = None
|
||||
|
||||
def Do(self):
|
||||
pyfalog.debug('Doing changing of projected fighter projection range to {} at position {} for fit {}'.format(
|
||||
self.projectionRange, self.position, self.fitID))
|
||||
fit = Fit.getInstance().getFit(self.fitID)
|
||||
fighter = fit.projectedFighters[self.position]
|
||||
if fighter.projectionRange == self.projectionRange:
|
||||
return False
|
||||
self.savedProjectionRange = fighter.projectionRange
|
||||
fighter.projectionRange = self.projectionRange
|
||||
return True
|
||||
|
||||
def Undo(self):
|
||||
pyfalog.debug('Undoing changing of projected fighter projection range to {} at position {} for fit {}'.format(
|
||||
self.projectionRange, self.position, self.fitID))
|
||||
cmd = CalcChangeProjectedFighterProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
projectionRange=self.savedProjectionRange)
|
||||
return cmd.Do()
|
||||
@@ -0,0 +1,55 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from gui.fitCommands.helpers import restoreCheckedStates
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class CalcChangeProjectedModuleProjectionRangeCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, position, projectionRange):
|
||||
wx.Command.__init__(self, True)
|
||||
self.fitID = fitID
|
||||
self.position = position
|
||||
self.projectionRange = projectionRange
|
||||
self.savedProjectionRange = None
|
||||
self.savedStateCheckChanges = None
|
||||
|
||||
def Do(self):
|
||||
pyfalog.debug('Doing change of projected module projection range at position {} to range {} on fit {}'.format(
|
||||
self.position, self.projectionRange, self.fitID))
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
mod = fit.projectedModules[self.position]
|
||||
if mod.projectionRange == self.projectionRange:
|
||||
return False
|
||||
self.savedProjectionRange = mod.projectionRange
|
||||
mod.projectionRange = self.projectionRange
|
||||
|
||||
sFit.recalc(fit)
|
||||
self.savedStateCheckChanges = sFit.checkStates(fit, mod)
|
||||
return True
|
||||
|
||||
def Undo(self):
|
||||
pyfalog.debug('Undoing change of projected module projection range at position {} to range {} on fit {}'.format(
|
||||
self.position, self.projectionRange, self.fitID))
|
||||
cmd = CalcChangeProjectedModuleProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
position=self.position,
|
||||
projectionRange=self.savedProjectionRange)
|
||||
result = cmd.Do()
|
||||
restoreCheckedStates(Fit.getInstance().getFit(self.fitID), self.savedStateCheckChanges)
|
||||
return result
|
||||
|
||||
@property
|
||||
def needsGuiRecalc(self):
|
||||
if self.savedStateCheckChanges is None:
|
||||
return True
|
||||
for container in self.savedStateCheckChanges:
|
||||
if len(container) > 0:
|
||||
return True
|
||||
return False
|
||||
60
gui/fitCommands/calc/projectedFit/changeProjectionRange.py
Normal file
60
gui/fitCommands/calc/projectedFit/changeProjectionRange.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from gui.fitCommands.helpers import restoreCheckedStates
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class CalcChangeProjectedFitProjectionRangeCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, projectedFitID, projectionRange):
|
||||
wx.Command.__init__(self, True, 'Change Projected Fit Projection Range')
|
||||
self.fitID = fitID
|
||||
self.projectedFitID = projectedFitID
|
||||
self.projectionRange = projectionRange
|
||||
self.savedProjectionRange = None
|
||||
self.savedStateCheckChanges = None
|
||||
|
||||
def Do(self):
|
||||
pyfalog.debug('Doing change of projected fit {} range to {} for fit {}'.format(self.projectedFitID, self.projectionRange, self.fitID))
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.fitID)
|
||||
projectedFit = sFit.getFit(self.projectedFitID, projected=True)
|
||||
# Projected fit could have been deleted if we are redoing
|
||||
if projectedFit is None:
|
||||
pyfalog.debug('Projected fit is not available')
|
||||
return False
|
||||
projectionInfo = projectedFit.getProjectionInfo(self.fitID)
|
||||
if projectionInfo is None:
|
||||
pyfalog.warning('Fit projection info is not available')
|
||||
return False
|
||||
if projectionInfo.projectionRange == self.projectionRange:
|
||||
return False
|
||||
self.savedProjectionRange = projectionInfo.projectionRange
|
||||
projectionInfo.projectionRange = self.projectionRange
|
||||
|
||||
sFit.recalc(fit)
|
||||
self.savedStateCheckChanges = sFit.checkStates(fit, None)
|
||||
return True
|
||||
|
||||
def Undo(self):
|
||||
pyfalog.debug('Undoing change of projected fit {} range to {} for fit {}'.format(self.projectedFitID, self.projectionRange, self.fitID))
|
||||
cmd = CalcChangeProjectedFitProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
projectedFitID=self.projectedFitID,
|
||||
projectionRange=self.savedProjectionRange)
|
||||
result = cmd.Do()
|
||||
restoreCheckedStates(Fit.getInstance().getFit(self.fitID), self.savedStateCheckChanges)
|
||||
return result
|
||||
|
||||
@property
|
||||
def needsGuiRecalc(self):
|
||||
if self.savedStateCheckChanges is None:
|
||||
return True
|
||||
for container in self.savedStateCheckChanges:
|
||||
if len(container) > 0:
|
||||
return True
|
||||
return False
|
||||
91
gui/fitCommands/gui/projectedChangeProjectionRange.py
Normal file
91
gui/fitCommands/gui/projectedChangeProjectionRange.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import wx
|
||||
|
||||
import eos.db
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.drone import Drone as EosDrone
|
||||
from eos.saveddata.fighter import Fighter as EosFighter
|
||||
from eos.saveddata.fit import Fit as EosFit
|
||||
from eos.saveddata.module import Module as EosModule
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.projectedChangeProjectionRange import CalcChangeProjectedDroneProjectionRangeCommand
|
||||
from gui.fitCommands.calc.fighter.projectedChangeProjectionRange import CalcChangeProjectedFighterProjectionRangeCommand
|
||||
from gui.fitCommands.calc.module.projectedChangeProjectionRange import CalcChangeProjectedModuleProjectionRangeCommand
|
||||
from gui.fitCommands.calc.projectedFit.changeProjectionRange import CalcChangeProjectedFitProjectionRangeCommand
|
||||
from gui.fitCommands.helpers import InternalCommandHistory
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class GuiChangeProjectedItemsProjectionRangeCommand(wx.Command):
|
||||
|
||||
def __init__(self, fitID, items, projectionRange):
|
||||
wx.Command.__init__(self, True, 'Change Projected Items Projection Range')
|
||||
self.internalHistory = InternalCommandHistory()
|
||||
self.fitID = fitID
|
||||
self.projectionRange = projectionRange
|
||||
self.pModPositions = []
|
||||
self.pDroneItemIDs = []
|
||||
self.pFighterPositions = []
|
||||
self.pFitIDs = []
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
for item in items:
|
||||
if isinstance(item, EosModule):
|
||||
if item in fit.projectedModules and not getattr(item, 'isExclusiveSystemEffect', False):
|
||||
self.pModPositions.append(fit.projectedModules.index(item))
|
||||
elif isinstance(item, EosDrone):
|
||||
self.pDroneItemIDs.append(item.itemID)
|
||||
elif isinstance(item, EosFighter):
|
||||
if item in fit.projectedFighters:
|
||||
self.pFighterPositions.append(fit.projectedFighters.index(item))
|
||||
elif isinstance(item, EosFit):
|
||||
self.pFitIDs.append(item.ID)
|
||||
|
||||
def Do(self):
|
||||
results = []
|
||||
needRecalc = True
|
||||
for pModPosition in self.pModPositions:
|
||||
cmd = CalcChangeProjectedModuleProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
position=pModPosition,
|
||||
projectionRange=self.projectionRange)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = cmd.needsGuiRecalc
|
||||
for pDroneItemID in self.pDroneItemIDs:
|
||||
cmd = CalcChangeProjectedDroneProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
itemID=pDroneItemID,
|
||||
projectionRange=self.projectionRange)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFighterPosition in self.pFighterPositions:
|
||||
cmd = CalcChangeProjectedFighterProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
position=pFighterPosition,
|
||||
projectionRange=self.projectionRange)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFitID in self.pFitIDs:
|
||||
cmd = CalcChangeProjectedFitProjectionRangeCommand(
|
||||
fitID=self.fitID,
|
||||
projectedFitID=pFitID,
|
||||
projectionRange=self.projectionRange)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = cmd.needsGuiRecalc
|
||||
success = any(results)
|
||||
sFit = Fit.getInstance()
|
||||
if needRecalc:
|
||||
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
|
||||
@@ -72,18 +72,21 @@ class GuiChangeProjectedItemStatesCommand(wx.Command):
|
||||
itemID=pDroneItemID,
|
||||
state=False if self.proposedState == 'inactive' else True)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFighterPosition in self.pFighterPositions:
|
||||
cmd = CalcChangeProjectedFighterStateCommand(
|
||||
fitID=self.fitID,
|
||||
position=pFighterPosition,
|
||||
state=False if self.proposedState == 'inactive' else True)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFitID in self.pFitIDs:
|
||||
cmd = CalcChangeProjectedFitStateCommand(
|
||||
fitID=self.fitID,
|
||||
projectedFitID=pFitID,
|
||||
state=False if self.proposedState == 'inactive' else True)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = cmd.needsGuiRecalc
|
||||
success = any(results)
|
||||
sFit = Fit.getInstance()
|
||||
if needRecalc:
|
||||
|
||||
@@ -7,7 +7,7 @@ import gui.mainFrame
|
||||
from gui import globalEvents as GE
|
||||
from gui.fitCommands.calc.drone.projectedChangeAmount import CalcChangeProjectedDroneAmountCommand
|
||||
from gui.fitCommands.calc.drone.projectedRemove import CalcRemoveProjectedDroneCommand
|
||||
from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
|
||||
from gui.fitCommands.helpers import InternalCommandHistory
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
|
||||
@@ -49,12 +49,15 @@ class GuiRemoveProjectedItemsCommand(wx.Command):
|
||||
for pDroneItemID in self.pDroneItemIDs:
|
||||
cmd = CalcRemoveProjectedDroneCommand(fitID=self.fitID, itemID=pDroneItemID, amount=self.amount)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFighterPosition in sorted(self.pFighterPositions, reverse=True):
|
||||
cmd = CalcRemoveProjectedFighterCommand(fitID=self.fitID, position=pFighterPosition)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = True
|
||||
for pFitID in self.pFitIDs:
|
||||
cmd = CalcRemoveProjectedFitCommand(fitID=self.fitID, projectedFitID=pFitID, amount=self.amount)
|
||||
results.append(self.internalHistory.submit(cmd))
|
||||
needRecalc = cmd.needsGuiRecalc
|
||||
success = any(results)
|
||||
sFit = Fit.getInstance()
|
||||
if needRecalc:
|
||||
|
||||
@@ -4,6 +4,7 @@ import wx.lib.newevent
|
||||
FitRenamed, FIT_RENAMED = wx.lib.newevent.NewEvent()
|
||||
FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent()
|
||||
FitRemoved, FIT_REMOVED = wx.lib.newevent.NewEvent()
|
||||
FitNotesChanged, FIT_NOTES_CHANGED = wx.lib.newevent.NewEvent()
|
||||
CharListUpdated, CHAR_LIST_UPDATED = wx.lib.newevent.NewEvent()
|
||||
CharChanged, CHAR_CHANGED = wx.lib.newevent.NewEvent()
|
||||
GraphOptionChanged, GRAPH_OPTION_CHANGED = wx.lib.newevent.NewEvent()
|
||||
|
||||
@@ -133,9 +133,7 @@ class ItemStatsFrame(AuxiliaryFrame):
|
||||
self.parentWnd.SetActiveStatsWindow(self)
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
@@ -162,7 +162,7 @@ class MainFrame(wx.Frame):
|
||||
mainSizer.Add(self.browser_fitting_split, 1, wx.EXPAND | wx.LEFT, 2)
|
||||
|
||||
self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split)
|
||||
self.additionsPane = AdditionsPane(self.fitting_additions_split)
|
||||
self.additionsPane = AdditionsPane(self.fitting_additions_split, self)
|
||||
|
||||
self.notebookBrowsers = ChromeNotebook(self.browser_fitting_split, False)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import gui.builtinViews.emptyView
|
||||
|
||||
class MultiSwitch(ChromeNotebook):
|
||||
def __init__(self, parent):
|
||||
ChromeNotebook.__init__(self, parent)
|
||||
ChromeNotebook.__init__(self, parent, can_add=True, tabWidthMode=1)
|
||||
# self.AddPage() # now handled by mainFrame
|
||||
self.handlers = handlers = []
|
||||
for type in TabSpawner.tabTypes:
|
||||
|
||||
@@ -281,9 +281,7 @@ class DmgPatternEditor(AuxiliaryFrame):
|
||||
self.stNotice.SetLabel("Patterns exported to clipboard")
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user