Compare commits

..

76 Commits

Author SHA1 Message Date
DarkPhoenix
e9c41c6439 Add renders of 2/3 of new ships 2020-06-06 01:42:57 +03:00
DarkPhoenix
b24ceb95f5 Add missing icons 2020-06-05 22:22:03 +03:00
DarkPhoenix
6ba6e0f69b Implement skill and booster effects, and bump version 2020-06-06 01:09:55 +03:00
DarkPhoenix
9aca49e539 Add damage mod effect and gun application readout 2020-06-06 00:25:19 +03:00
DarkPhoenix
01f1cdb175 Implement gun effect and its support in graphs 2020-06-06 00:11:36 +03:00
DarkPhoenix
c1356906bb Add ship effects 2020-06-05 23:36:39 +03:00
DarkPhoenix
8229537e5f Run effect used by script 2020-06-05 22:50:34 +03:00
DarkPhoenix
6cfa2161ec Add conversions for amplifier tiericide 2020-06-05 22:47:13 +03:00
DarkPhoenix
fa3cf46786 Update static data to 1742848 2020-06-05 22:28:25 +03:00
DarkPhoenix
ac40dfd018 Update version again 2020-05-26 16:07:01 +03:00
DarkPhoenix
4c32d559bb Bump version 2020-05-26 16:04:51 +03:00
DarkPhoenix
02ee12586f Adjust jargon definitions to new realities 2020-05-26 16:03:48 +03:00
DarkPhoenix
496f15a83e Add conversions/migrations 2020-05-26 15:50:52 +03:00
DarkPhoenix
acf6af8f95 Update effect definitions and headers to all the renamed items 2020-05-26 15:30:50 +03:00
DarkPhoenix
225b00b9ff Update staticdata to 1733182 and update DB generation script 2020-05-26 15:22:08 +03:00
DarkPhoenix
7f2b7415ea Disable evemarketdata price source 2020-05-14 15:10:04 +03:00
DarkPhoenix
d1d9ae4dac Add fuzzwork as price data source 2020-05-14 15:09:38 +03:00
DarkPhoenix
ac7ccfb6a3 Change how double-clicking ammo behaves 2020-05-04 19:18:14 +03:00
DarkPhoenix
05c2c41762 Destroy effective label when switching from firepower to mining view, and add it back to correct position when needed 2020-05-04 13:46:52 +03:00
DarkPhoenix
10f5133282 Revert "Disable system theme for listctrls on windows"
This reverts commit 42658a8167.
2020-04-27 13:55:30 +03:00
DarkPhoenix
42658a8167 Disable system theme for listctrls on windows 2020-04-26 23:27:28 +03:00
DarkPhoenix
6dbf38dbb4 Revert "Switch to wxpython 4.1.0". Refer to #2179 for more info on
reasons

This reverts commit 15fe8f6261.
2020-04-25 17:37:29 +03:00
DarkPhoenix
64306e2366 Align attribute icons the same way as input boxes in target profile
editor
2020-04-25 17:29:29 +03:00
DarkPhoenix
f259df85cd Fix editors 2020-04-25 17:27:29 +03:00
DarkPhoenix
1090589cb2 Fix preferences for wx 4.1.0 2020-04-25 17:19:05 +03:00
DarkPhoenix
29e09fcdda Fix abyssal item slider on windows 2020-04-25 12:36:45 +03:00
DarkPhoenix
36fd4aeaff Redo fix for 2176 2020-04-25 15:14:27 +03:00
DarkPhoenix
c46a59e3b1 Fix gauge bars 2020-04-25 09:57:49 +03:00
DarkPhoenix
8a3a21972a Fix error dialog layout 2020-04-25 09:40:50 +03:00
DarkPhoenix
6839669e04 Remove a few flags the new wx complains about 2020-04-25 09:36:40 +03:00
DarkPhoenix
5645e20505 Do not float auxiliary windows on parent if there is no parent 2020-04-25 09:29:14 +03:00
DarkPhoenix
15fe8f6261 Switch to wxpython 4.1.0 2020-04-25 09:26:47 +03:00
DarkPhoenix
855fafa94d Bump version 2020-04-24 15:35:52 +03:00
DarkPhoenix
4e10335ae7 Revert "Try resetting locale on wx 4.0.6 as an attempt to work around #2174"
This reverts commit ea07bbf4f9.
2020-04-24 15:10:33 +03:00
DarkPhoenix
21ea9ce579 "Move" attributes at DB generation time 2020-04-24 15:10:00 +03:00
DarkPhoenix
ea07bbf4f9 Try resetting locale on wx 4.0.6 as an attempt to work around #2174 2020-04-24 11:37:27 +03:00
DarkPhoenix
8eed6fbe21 Ignore mutations with 0 base value 2020-04-23 19:47:57 +03:00
DarkPhoenix
0859f2fbe9 Hide limited synth boosters 2020-04-22 02:27:20 +03:00
DarkPhoenix
71ba33edeb Fix tail clear method 2020-04-21 19:00:44 +03:00
DarkPhoenix
ce80d92b35 Limit amount of fits returned by search by 100 2020-04-21 11:16:06 +03:00
DarkPhoenix
f17cf9b736 Fix undo for fill with module (which was caused by module add command not being undone properly in case of failure) 2020-04-20 16:36:16 +03:00
DarkPhoenix
98579c774b Change shortcuts for undo/redo from built-in wx standards to ctrl-z and ctrl-y 2020-04-20 14:42:32 +03:00
DarkPhoenix
509fa279e7 Initialize session container with main thread session 2020-04-17 19:15:47 +03:00
DarkPhoenix
091ee87761 Update char list when new character gets added 2020-04-17 13:31:15 +03:00
DarkPhoenix
c0c20cc92e Remove skill refresh button 2020-04-17 13:27:51 +03:00
DarkPhoenix
1341f7bca1 Make some jargon replacements a bit looser 2020-04-17 11:27:28 +03:00
DarkPhoenix
fe93db1d4b Round booster side-effect context menu 2020-04-17 00:25:33 +03:00
DarkPhoenix
5db97ea773 Bump version 2020-04-16 22:46:45 +03:00
DarkPhoenix
1758e4f320 Rework fix for projection, so that it does not recalculate projected fits too early 2020-04-16 22:25:10 +03:00
DarkPhoenix
1a897c0419 Pass search results as set of item IDs 2020-04-16 15:13:32 +03:00
DarkPhoenix
32db3e3179 Create scoped gamedata sessions 2020-04-16 15:01:00 +03:00
DarkPhoenix
d830a8957a Bump version 2020-04-15 16:54:24 +03:00
DarkPhoenix
652ea48223 Accept 0 free slots for guns which have already been fitted 2020-04-15 16:53:03 +03:00
DarkPhoenix
8c25b2b8f5 Make sure it's impossible to add extra cap boosters via dragging too 2020-04-15 15:40:54 +03:00
DarkPhoenix
db4c56be8e Bump version 2020-04-15 15:27:19 +03:00
DarkPhoenix
f3bcffe2f9 Implement fax cap booster limit 2020-04-15 15:23:49 +03:00
DarkPhoenix
bc5786d099 Update static data to 1706308 2020-04-15 14:50:55 +03:00
DarkPhoenix
5959fe5daf Ignore case when searching for implant sets to allow some inconsistencies on CCP side 2020-04-14 15:28:40 +03:00
DarkPhoenix
649d338bb1 Split implants and boosters in EFT and multibuy exports 2020-04-14 11:36:08 +03:00
DarkPhoenix
dcb058a718 Update existing character if character with the same name exists 2020-04-13 12:52:02 +03:00
DarkPhoenix
1772bb5e7f Merge branch 'master' into singularity 2020-04-13 12:45:25 +03:00
Anton Vorobyov
30bd0adb06 Merge pull request #2159 from soro/auto_create_char
Regarding #2045: automatically create a character for a new sso character
2020-04-12 22:49:01 +03:00
DarkPhoenix
44dfcf771c Ensure projected fits get recalculated, since they get checkStates'd anyway 2020-04-11 02:26:17 +03:00
DarkPhoenix
a1f8a7a930 Fix escaping in regex search 2020-04-11 01:33:53 +03:00
Soeren Roerden
b22887dfad automatically create a character for a new sso character and add button to retrieve all chars directly linked to existing sso characters 2020-04-10 21:12:38 +02:00
DarkPhoenix
28137fa3f4 Remove excessive space 2020-04-10 13:11:04 +03:00
DarkPhoenix
9cbdc6055d Add search timer to attribute overrides to prevent hangs 2020-04-10 11:52:57 +03:00
DarkPhoenix
fc93c61fcf Add more entries 2020-04-10 11:31:17 +03:00
DarkPhoenix
3fa2e7ebd1 More additions to jargon 2020-04-10 06:07:33 +03:00
DarkPhoenix
818628da0c Finalize new jargon dictionary 2020-04-10 05:53:24 +03:00
DarkPhoenix
adf90a8263 Split basic and regex search functions 2020-04-10 00:57:41 +03:00
DarkPhoenix
362923ac64 Overhaul jargon dictionary (some entries are still missing) 2020-04-09 23:29:51 +03:00
DarkPhoenix
7d73838ce1 Change the way user definitions are used 2020-04-08 14:06:49 +03:00
DarkPhoenix
b3278ca9ec Tokenize regexp requests taking into consideration regexp context 2020-04-08 13:24:48 +03:00
DarkPhoenix
5707914ad5 Make use of regex for search 2020-04-08 03:17:25 +03:00
DarkPhoenix
9b697b24d8 Implement regexp function for gamedata 2020-04-08 02:34:28 +03:00
3298 changed files with 95860 additions and 77054 deletions

View File

@@ -125,6 +125,8 @@ def update_db():
row['typeName'] in ('Capsule', 'Dark Blood Tracking Disruptor')
):
row['published'] = True
elif row['typeName'].startswith('Limited Synth '):
row['published'] = False
newData = []
for row in data:
@@ -168,16 +170,26 @@ def update_db():
data = _readData('fsd_binary', 'typedogma', keyIdName='typeID')
eveTypeIds = set(r['typeID'] for r in eveTypesData)
newData = []
for row in eveTypesData:
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
if attrName in row:
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
seenKeys = set()
def checkKey(key):
if key in seenKeys:
return False
seenKeys.add(key)
return True
for typeData in data:
if typeData['typeID'] not in eveTypeIds:
continue
for row in typeData.get('dogmaAttributes', ()):
row['typeID'] = typeData['typeID']
newData.append(row)
if checkKey((row['typeID'], row['attributeID'])):
newData.append(row)
for row in eveTypesData:
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
if attrName in row and checkKey((row['typeID'], attrId)):
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
_addRows(newData, eos.gamedata.Attribute)
return newData
@@ -315,8 +327,8 @@ def update_db():
def composeReqSkills(raw):
reqSkills = {}
for skillTypeID, skillLevels in raw.items():
reqSkills[int(skillTypeID)] = skillLevels[0]
for skillTypeID, skillLevel in raw.items():
reqSkills[int(skillTypeID)] = skillLevel
return reqSkills
eveTypeIds = set(r['typeID'] for r in eveTypesData)
@@ -472,7 +484,7 @@ def update_db():
continue
typeName = row.get('typeName', '')
# Regular sets matching
m = re.match('(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName)
m = re.match('(?P<grade>(High|Mid|Low)-grade) (?P<set>\w+) (?P<implant>(Alpha|Beta|Gamma|Delta|Epsilon|Omega))', typeName, re.IGNORECASE)
if m:
implantSets.setdefault((m.group('grade'), m.group('set')), set()).add(row['typeID'])
# Special set matching

View File

@@ -17,24 +17,37 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import re
import threading
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import MetaData, create_engine, event
from sqlalchemy.orm import sessionmaker, scoped_session
from . import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
pyfalog.info("Initializing database")
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
class ReadOnlyException(Exception):
pass
def re_fn(expr, item):
try:
reg = re.compile(expr, re.IGNORECASE)
except (SystemExit, KeyboardInterrupt):
raise
except:
return False
return reg.search(item) is not None
pyfalog.debug('Initializing gamedata')
gamedata_connectionstring = config.gamedata_connectionstring
if callable(gamedata_connectionstring):
@@ -42,9 +55,26 @@ if callable(gamedata_connectionstring):
else:
gamedata_engine = create_engine(gamedata_connectionstring, echo=config.debug)
@event.listens_for(gamedata_engine, 'connect')
def create_functions(dbapi_connection, connection_record):
dbapi_connection.create_function('regexp', 2, re_fn)
gamedata_meta = MetaData()
gamedata_meta.bind = gamedata_engine
gamedata_session = sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False)()
GamedataSession = scoped_session(sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False))
gamedata_session = GamedataSession()
gamedata_sessions = {threading.get_ident(): gamedata_session}
def get_gamedata_session():
thread_id = threading.get_ident()
if thread_id not in gamedata_sessions:
gamedata_sessions[thread_id] = GamedataSession()
return gamedata_sessions[thread_id]
pyfalog.debug('Getting gamedata version')
# This should be moved elsewhere, maybe as an actual query. Current, without try-except, it breaks when making a new

View File

@@ -33,9 +33,6 @@ items_table = Table("invtypes", gamedata_meta,
Column("description", String),
Column("raceID", Integer),
Column("factionID", Integer),
Column("volume", Float),
Column("mass", Float),
Column("capacity", Float),
Column("published", Boolean),
Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")),
Column("iconID", Integer),

View File

@@ -22,7 +22,7 @@ from sqlalchemy.orm import aliased, exc, join
from sqlalchemy.sql import and_, or_, select
import eos.config
from eos.db import gamedata_session
from eos.db import get_gamedata_session
from eos.db.gamedata.item import items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
@@ -64,7 +64,7 @@ else:
return deco
def sqlizeString(line):
def sqlizeNormalString(line):
# Escape backslashes first, as they will be as escape symbol in queries
# Then escape percent and underscore signs
# Finally, replace generic wildcards with sql-style wildcards
@@ -79,29 +79,39 @@ itemNameMap = {}
def getItem(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
item = gamedata_session.query(Item).get(lookfor)
item = get_gamedata_session().query(Item).get(lookfor)
else:
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in itemNameMap:
id = itemNameMap[lookfor]
if eager is None:
item = gamedata_session.query(Item).get(id)
item = get_gamedata_session().query(Item).get(id)
else:
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID == id).first()
else:
# Item names are unique, so we can use first() instead of one()
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
item = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
if item is not None:
itemNameMap[lookfor] = item.ID
else:
raise TypeError("Need integer or string as argument")
return item
@cachedQuery(1, "itemIDs")
def getItems(itemIDs, eager=None):
if not isinstance(itemIDs, (tuple, list, set)) or not all(isinstance(t, int) for t in itemIDs):
raise TypeError("Need iterable of integers as argument")
if eager is None:
items = get_gamedata_session().query(Item).filter(Item.ID.in_(itemIDs)).all()
else:
items = get_gamedata_session().query(Item).options(*processEager(eager)).filter(Item.ID.in_(itemIDs)).all()
return items
def getMutaplasmid(lookfor, eager=None):
if isinstance(lookfor, int):
item = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
item = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
return item
@@ -109,7 +119,7 @@ def getMutaplasmid(lookfor, eager=None):
def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
# A lot of this is described in more detail in #1597
item = gamedata_session.query(Item).get(lookfor)
item = get_gamedata_session().query(Item).get(lookfor)
base = getItem(baseItemID)
# we have to load all attributes for this object, otherwise we'll lose access to them when we expunge.
@@ -125,7 +135,7 @@ def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None):
# Expunge the item form the session. This is required to have different Abyssal / Base combinations loaded in memory.
# Without expunging it, once one Abyssal Web is created, SQLAlchmey will use it for all others. We don't want this,
# we want to generate a completely new object to work with
gamedata_session.expunge(item)
get_gamedata_session().expunge(item)
return item
@@ -148,7 +158,7 @@ def getItems(lookfor, eager=None):
if len(toGet) > 0:
# Get items that aren't currently cached, and store them in the cache
items = gamedata_session.query(Item).filter(Item.ID.in_(toGet)).all()
items = get_gamedata_session().query(Item).filter(Item.ID.in_(toGet)).all()
for item in items:
cache[(item.ID, None)] = item
results += items
@@ -162,9 +172,9 @@ def getItems(lookfor, eager=None):
def getAlphaClone(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
item = gamedata_session.query(AlphaClone).get(lookfor)
item = get_gamedata_session().query(AlphaClone).get(lookfor)
else:
item = gamedata_session.query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
item = get_gamedata_session().query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
return item
@@ -172,7 +182,7 @@ def getAlphaClone(lookfor, eager=None):
def getAlphaCloneList(eager=None):
eager = processEager(eager)
clones = gamedata_session.query(AlphaClone).options(*eager).all()
clones = get_gamedata_session().query(AlphaClone).options(*eager).all()
return clones
@@ -183,19 +193,19 @@ groupNameMap = {}
def getGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
group = gamedata_session.query(Group).get(lookfor)
group = get_gamedata_session().query(Group).get(lookfor)
else:
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in groupNameMap:
id = groupNameMap[lookfor]
if eager is None:
group = gamedata_session.query(Group).get(id)
group = get_gamedata_session().query(Group).get(id)
else:
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.ID == id).first()
else:
# Group names are unique, so we can use first() instead of one()
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
group = get_gamedata_session().query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
if group is not None:
groupNameMap[lookfor] = group.ID
else:
@@ -210,21 +220,21 @@ categoryNameMap = {}
def getCategory(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
category = gamedata_session.query(Category).get(lookfor)
category = get_gamedata_session().query(Category).get(lookfor)
else:
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in categoryNameMap:
id = categoryNameMap[lookfor]
if eager is None:
category = gamedata_session.query(Category).get(id)
category = get_gamedata_session().query(Category).get(id)
else:
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.ID == id).first()
else:
# Category names are unique, so we can use first() instead of one()
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
category = get_gamedata_session().query(Category).options(*processEager(eager)).filter(
Category.name == lookfor).first()
if category is not None:
categoryNameMap[lookfor] = category.ID
@@ -240,21 +250,21 @@ metaGroupNameMap = {}
def getMetaGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
metaGroup = gamedata_session.query(MetaGroup).get(lookfor)
metaGroup = get_gamedata_session().query(MetaGroup).get(lookfor)
else:
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.ID == lookfor).first()
elif isinstance(lookfor, str):
if lookfor in metaGroupNameMap:
id = metaGroupNameMap[lookfor]
if eager is None:
metaGroup = gamedata_session.query(MetaGroup).get(id)
metaGroup = get_gamedata_session().query(MetaGroup).get(id)
else:
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.ID == id).first()
else:
# MetaGroup names are unique, so we can use first() instead of one()
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
metaGroup = get_gamedata_session().query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.name == lookfor).first()
if metaGroup is not None:
metaGroupNameMap[lookfor] = metaGroup.ID
@@ -264,16 +274,16 @@ def getMetaGroup(lookfor, eager=None):
def getMetaGroups():
return gamedata_session.query(MetaGroup).all()
return get_gamedata_session().query(MetaGroup).all()
@cachedQuery(1, "lookfor")
def getMarketGroup(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
marketGroup = gamedata_session.query(MarketGroup).get(lookfor)
marketGroup = get_gamedata_session().query(MarketGroup).get(lookfor)
else:
marketGroup = gamedata_session.query(MarketGroup).options(*processEager(eager)).filter(
marketGroup = get_gamedata_session().query(MarketGroup).options(*processEager(eager)).filter(
MarketGroup.ID == lookfor).first()
else:
raise TypeError("Need integer as argument")
@@ -285,7 +295,7 @@ def getMarketTreeNodeIds(rootNodeIds):
addedIds = set(rootNodeIds)
while addedIds:
allIds.update(addedIds)
addedIds = {mg.ID for mg in gamedata_session.query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
addedIds = {mg.ID for mg in get_gamedata_session().query(MarketGroup).filter(MarketGroup.parentGroupID.in_(addedIds))}
return allIds
@@ -299,7 +309,7 @@ def getItemsByCategory(filter, where=None, eager=None):
raise TypeError("Need integer or string as argument")
filter = processWhere(filter, where)
return gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
return get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category).filter(
filter).all()
@@ -314,9 +324,9 @@ def searchItems(nameLike, where=None, join=None, eager=None):
if not hasattr(join, "__iter__"):
join = (join,)
items = gamedata_session.query(Item).options(*processEager(eager)).join(*join)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
for token in nameLike.split(' '):
token_safe = "%{0}%".format(sqlizeString(token))
token_safe = "%{0}%".format(sqlizeNormalString(token))
if where is not None:
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where))
else:
@@ -325,14 +335,35 @@ def searchItems(nameLike, where=None, join=None, eager=None):
return items
@cachedQuery(3, "tokens", "where", "join")
def searchItemsRegex(tokens, where=None, join=None, eager=None):
if not isinstance(tokens, (tuple, list)) or not all(isinstance(t, str) for t in tokens):
raise TypeError("Need tuple or list of strings as argument")
if join is None:
join = tuple()
if not hasattr(join, "__iter__"):
join = (join,)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(*join)
for token in tokens:
if where is not None:
items = items.filter(and_(Item.name.op('regexp')(token), where))
else:
items = items.filter(Item.name.op('regexp')(token))
items = items.limit(100).all()
return items
@cachedQuery(3, "where", "nameLike", "join")
def searchSkills(nameLike, where=None, eager=None):
if not isinstance(nameLike, str):
raise TypeError("Need string as argument")
items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category)
items = get_gamedata_session().query(Item).options(*processEager(eager)).join(Item.group, Group.category)
for token in nameLike.split(' '):
token_safe = "%{0}%".format(sqlizeString(token))
token_safe = "%{0}%".format(sqlizeNormalString(token))
if where is not None:
items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where))
else:
@@ -352,7 +383,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
itemfilter = or_(*(items_table.c.variationParentTypeID == itemid for itemid in itemids))
filter = processWhere(itemfilter, where)
vars = gamedata_session.query(Item).options(*processEager(eager)).filter(filter).all()
vars = get_gamedata_session().query(Item).options(*processEager(eager)).filter(filter).all()
if vars:
return vars
@@ -360,7 +391,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None):
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
filter = processWhere(itemfilter, where)
joinon = items_table.c.groupID == groups_table.c.groupID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
vars = get_gamedata_session().query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
filter).all()
return vars
@@ -375,7 +406,7 @@ def getAttributeInfo(attr, eager=None):
else:
raise TypeError("Need integer or string as argument")
try:
result = gamedata_session.query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
result = get_gamedata_session().query(AttributeInfo).options(*processEager(eager)).filter(filter).one()
except exc.NoResultFound:
result = None
return result
@@ -384,7 +415,7 @@ def getAttributeInfo(attr, eager=None):
@cachedQuery(1, "field")
def getMetaData(field):
if isinstance(field, str):
data = gamedata_session.query(MetaData).get(field)
data = get_gamedata_session().query(MetaData).get(field)
else:
raise TypeError("Need string as argument")
return data
@@ -403,12 +434,12 @@ def directAttributeRequest(itemIDs, attrIDs):
and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)),
from_obj=[join(Attribute, Item)])
result = gamedata_session.execute(q).fetchall()
result = get_gamedata_session().execute(q).fetchall()
return result
def getAbyssalTypes():
return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()])
return set([r.resultingTypeID for r in get_gamedata_session().query(DynamicItem.resultingTypeID).distinct()])
@cachedQuery(1, "itemID")
@@ -416,9 +447,9 @@ def getDynamicItem(itemID, eager=None):
try:
if isinstance(itemID, int):
if eager is None:
result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one()
result = get_gamedata_session().query(DynamicItem).filter(DynamicItem.ID == itemID).one()
else:
result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
result = get_gamedata_session().query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
else:
raise TypeError("Need integer as argument")
except exc.NoResultFound:
@@ -428,5 +459,5 @@ def getDynamicItem(itemID, eager=None):
@cachedQuery(1, "lookfor")
def getAllImplantSets():
implantSets = gamedata_session.query(ImplantSet).all()
implantSets = get_gamedata_session().query(ImplantSet).all()
return implantSets

View File

@@ -0,0 +1,42 @@
"""
Migration 38
- Armor hardener tiericide
"""
CONVERSIONS = {
16357: ( # Experimental Enduring EM Armor Hardener I
16353, # Upgraded Armor EM Hardener I
),
16365: ( # Experimental Enduring Explosive Armor Hardener I
16361, # Upgraded Armor Explosive Hardener I
),
16373: ( # Experimental Enduring Kinetic Armor Hardener I
16369, # Upgraded Armor Kinetic Hardener I
),
16381: ( # Experimental Enduring Thermal Armor Hardener I
16377, # Upgraded Armor Thermal Hardener I
),
16359: ( # Prototype Compact EM Armor Hardener I
16355, # Limited Armor EM Hardener I
),
16367: ( # Prototype Compact Explosive Armor Hardener I
16363, # Limited Armor Explosive Hardener I
),
16375: ( # Prototype Compact Kinetic Armor Hardener I
16371, # Limited Armor Kinetic Hardener I
),
16383: ( # Prototype Compact Thermal Armor Hardener I
16379, # Limited Armor Thermal Hardener I
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -0,0 +1,30 @@
"""
Migration 39
- Shield amplifier tiericide
"""
CONVERSIONS = {
1798: ( # 'Basic' EM Shield Amplifier
9562, # Supplemental EM Ward Amplifier
),
1804: ( # 'Basic' Explosive Shield Amplifier
9574, # Supplemental Explosive Deflection Amplifier
),
1802: ( # 'Basic' Kinetic Shield Amplifier
9570, # Supplemental Kinetic Deflection Amplifier
),
1800: ( # 'Basic' Thermal Shield Amplifier
9566, # Supplemental Thermal Dissipation Amplifier
)
}
def upgrade(saveddata_engine):
# Convert modules
for replacement_item, list in CONVERSIONS.items():
for retired_item in list:
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
(replacement_item, retired_item))

View File

@@ -470,7 +470,7 @@ def searchFits(nameLike, where=None, eager=None):
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
eager = processEager(eager)
with sd_lock:
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).limit(100).all())
return fits

View File

@@ -96,7 +96,7 @@ class Effect21(BaseEffect):
Used by:
Modules from group: Shield Extender (36 of 36)
Modules from group: Shield Resistance Amplifier (88 of 88)
Modules from group: Shield Resistance Amplifier (84 of 84)
"""
type = 'passive'
@@ -989,7 +989,7 @@ class Effect290(BaseEffect):
sharpshooterRangeSkillBonusPostPercentMaxRangeLocationShipModulesRequiringGunnery
Used by:
Implants named like: Frentix Booster (9 of 9)
Implants named like: Frentix Booster (4 of 4)
Implants named like: Zainou 'Deadeye' Sharpshooter ST (6 of 6)
Skill: Sharpshooter
"""
@@ -1008,7 +1008,7 @@ class Effect298(BaseEffect):
surgicalStrikeFalloffBonusPostPercentFalloffLocationShipModulesRequiringGunnery
Used by:
Implants named like: Sooth Sayer Booster (9 of 9)
Implants named like: Sooth Sayer Booster (4 of 4)
Implants named like: Zainou 'Deadeye' Trajectory Analysis TA (6 of 6)
Skill: Trajectory Analysis
"""
@@ -1633,7 +1633,7 @@ class Effect581(BaseEffect):
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
level = container.level if 'skill' in context else 1
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Gunnery'),
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Gunnery') or mod.item.requiresSkill('Vorton Projector Operation'),
'cpu', container.getModifiedItemAttr('cpuNeedBonus') * level, **kwargs)
@@ -1742,7 +1742,7 @@ class Effect596(BaseEffect):
ammoInfluenceRange
Used by:
Items from category: Charge (590 of 955)
Items from category: Charge (608 of 973)
"""
type = 'passive'
@@ -2328,7 +2328,7 @@ class Effect804(BaseEffect):
ammoInfluenceCapNeed
Used by:
Items from category: Charge (496 of 955)
Items from category: Charge (514 of 973)
"""
type = 'passive'
@@ -3694,7 +3694,7 @@ class Effect1185(BaseEffect):
structureStealthEmitterArraySigDecrease
Used by:
Implants named like: X Instinct Booster (9 of 9)
Implants named like: X Instinct Booster (4 of 4)
Implants named like: grade Halo (15 of 18)
"""
@@ -4898,7 +4898,9 @@ class Effect1638(BaseEffect):
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Gunnery') or mod.item.requiresSkill('Missile Launcher Operation'),
lambda mod: (mod.item.requiresSkill('Gunnery') or
mod.item.requiresSkill('Missile Launcher Operation') or
mod.item.requiresSkill('Vorton Projector Operation')),
'power', skill.getModifiedItemAttr('powerNeedBonus') * skill.level, **kwargs)
@@ -5789,7 +5791,7 @@ class Effect1959(BaseEffect):
armorReinforcerMassAdd
Used by:
Modules from group: Armor Reinforcer (51 of 51)
Modules from group: Armor Plate (51 of 51)
"""
type = 'passive'
@@ -6054,7 +6056,7 @@ class Effect2052(BaseEffect):
modifyShieldResonancePostPercent
Used by:
Modules from group: Shield Resistance Amplifier (88 of 88)
Modules from group: Shield Resistance Amplifier (84 of 84)
"""
type = 'passive'
@@ -6966,7 +6968,7 @@ class Effect2432(BaseEffect):
Used by:
Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6)
Implants named like: Mindflood Booster (9 of 9)
Implants named like: Mindflood Booster (4 of 4)
Modules named like: Semiconductor Memory Cell (8 of 8)
Implant: Antipharmakon Aeolis
Implant: Genolution Core Augmentation CA-1
@@ -7822,9 +7824,10 @@ class Effect2735(BaseEffect):
boosterArmorHpPenalty
Used by:
Implants named like: Improved Booster (4 of 8)
Implants named like: Standard Booster (4 of 8)
Implants named like: Strong Booster (4 of 8)
Implants named like: Crash Booster (3 of 4)
Implants named like: Exile Booster (3 of 4)
Implants named like: Frentix Booster (3 of 4)
Implants named like: X Instinct Booster (3 of 4)
"""
attr = 'boosterArmorHPPenalty'
@@ -7841,15 +7844,9 @@ class Effect2736(BaseEffect):
boosterArmorRepairAmountPenalty
Used by:
Implant: Improved Drop Booster
Implant: Improved Mindflood Booster
Implant: Improved Sooth Sayer Booster
Implant: Standard Drop Booster
Implant: Standard Mindflood Booster
Implant: Standard Sooth Sayer Booster
Implant: Strong Drop Booster
Implant: Strong Mindflood Booster
Implant: Strong Sooth Sayer Booster
Implants named like: Drop Booster (3 of 4)
Implants named like: Mindflood Booster (3 of 4)
Implants named like: Sooth Sayer Booster (3 of 4)
"""
attr = 'boosterArmorRepairAmountPenalty'
@@ -7867,9 +7864,10 @@ class Effect2737(BaseEffect):
boosterShieldCapacityPenalty
Used by:
Implants named like: Improved Booster (4 of 8)
Implants named like: Standard Booster (4 of 8)
Implants named like: Strong Booster (4 of 8)
Implants named like: Blue Pill Booster (3 of 5)
Implants named like: Drop Booster (3 of 4)
Implants named like: Sooth Sayer Booster (3 of 4)
Implants named like: X Instinct Booster (3 of 4)
"""
attr = 'boosterShieldCapacityPenalty'
@@ -7886,15 +7884,9 @@ class Effect2739(BaseEffect):
boosterTurretOptimalRangePenalty
Used by:
Implant: Improved Blue Pill Booster
Implant: Improved Mindflood Booster
Implant: Improved Sooth Sayer Booster
Implant: Standard Blue Pill Booster
Implant: Standard Mindflood Booster
Implant: Standard Sooth Sayer Booster
Implant: Strong Blue Pill Booster
Implant: Strong Mindflood Booster
Implant: Strong Sooth Sayer Booster
Implants named like: Blue Pill Booster (3 of 5)
Implants named like: Mindflood Booster (3 of 4)
Implants named like: Sooth Sayer Booster (3 of 4)
"""
attr = 'boosterTurretOptimalRangePenalty'
@@ -7912,12 +7904,8 @@ class Effect2741(BaseEffect):
boosterTurretFalloffPenalty
Used by:
Implant: Improved Drop Booster
Implant: Improved X-Instinct Booster
Implant: Standard Drop Booster
Implant: Standard X-Instinct Booster
Implant: Strong Drop Booster
Implant: Strong X-Instinct Booster
Implants named like: Drop Booster (3 of 4)
Implants named like: X Instinct Booster (3 of 4)
"""
attr = 'boosterTurretFalloffPenalty'
@@ -7935,12 +7923,8 @@ class Effect2745(BaseEffect):
boosterCapacitorCapacityPenalty
Used by:
Implant: Improved Blue Pill Booster
Implant: Improved Exile Booster
Implant: Standard Blue Pill Booster
Implant: Standard Exile Booster
Implant: Strong Blue Pill Booster
Implant: Strong Exile Booster
Implants named like: Blue Pill Booster (3 of 5)
Implants named like: Exile Booster (3 of 4)
"""
attr = 'boosterCapacitorCapacityPenalty'
@@ -7957,10 +7941,8 @@ class Effect2746(BaseEffect):
boosterMaxVelocityPenalty
Used by:
Implants named like: Crash Booster (3 of 4)
Items from market group: Implants & Boosters > Booster > Booster Slot 02 (9 of 13)
Implant: Improved Crash Booster
Implant: Standard Crash Booster
Implant: Strong Crash Booster
"""
attr = 'boosterMaxVelocityPenalty'
@@ -7977,12 +7959,8 @@ class Effect2747(BaseEffect):
boosterTurretTrackingPenalty
Used by:
Implant: Improved Exile Booster
Implant: Improved Frentix Booster
Implant: Standard Exile Booster
Implant: Standard Frentix Booster
Implant: Strong Exile Booster
Implant: Strong Frentix Booster
Implants named like: Exile Booster (3 of 4)
Implants named like: Frentix Booster (3 of 4)
"""
attr = 'boosterTurretTrackingPenalty'
@@ -8000,12 +7978,8 @@ class Effect2748(BaseEffect):
boosterMissileVelocityPenalty
Used by:
Implant: Improved Crash Booster
Implant: Improved X-Instinct Booster
Implant: Standard Crash Booster
Implant: Standard X-Instinct Booster
Implant: Strong Crash Booster
Implant: Strong X-Instinct Booster
Implants named like: Crash Booster (3 of 4)
Implants named like: X Instinct Booster (3 of 4)
"""
attr = 'boosterMissileVelocityPenalty'
@@ -8023,9 +7997,7 @@ class Effect2749(BaseEffect):
boosterMissileExplosionVelocityPenalty
Used by:
Implant: Improved Blue Pill Booster
Implant: Standard Blue Pill Booster
Implant: Strong Blue Pill Booster
Implants named like: Blue Pill Booster (3 of 5)
"""
attr = 'boosterAOEVelocityPenalty'
@@ -8180,12 +8152,8 @@ class Effect2791(BaseEffect):
boosterMissileExplosionCloudPenaltyFixed
Used by:
Implant: Improved Exile Booster
Implant: Improved Mindflood Booster
Implant: Standard Exile Booster
Implant: Standard Mindflood Booster
Implant: Strong Exile Booster
Implant: Strong Mindflood Booster
Implants named like: Exile Booster (3 of 4)
Implants named like: Mindflood Booster (3 of 4)
"""
attr = 'boosterMissileAOECloudPenalty'
@@ -8203,7 +8171,7 @@ class Effect2792(BaseEffect):
modifyArmorResonancePostPercentPassive
Used by:
Modules named like: Anti Pump (32 of 32)
Modules named like: Armor Reinforcer (32 of 32)
"""
type = 'passive'
@@ -8239,7 +8207,7 @@ class Effect2795(BaseEffect):
modifyShieldResonancePostPercentPassive
Used by:
Modules named like: Anti Screen Reinforcer (32 of 32)
Modules named like: Shield Reinforcer (32 of 32)
"""
type = 'passive'
@@ -8462,7 +8430,7 @@ class Effect2837(BaseEffect):
armorHPBonusAdd
Used by:
Modules from group: Armor Reinforcer (51 of 51)
Modules from group: Armor Plate (51 of 51)
"""
type = 'passive'
@@ -8477,7 +8445,8 @@ class Effect2847(BaseEffect):
trackingSpeedBonusPassiveRequiringGunneryTrackingSpeedBonus
Used by:
Implants named like: Drop Booster (9 of 9)
Implants named like: Drop Booster (4 of 4)
Implants named like: EDENCOM Vorton Booster (6 of 9)
Implants named like: Eifyr and Co. 'Gunslinger' Motion Prediction MR (6 of 6)
Implant: Antipharmakon Iokira
Implant: Ogdin's Eye Coordination Enhancer
@@ -9213,7 +9182,7 @@ class Effect3001(BaseEffect):
Used by:
Modules from group: Missile Launcher Torpedo (22 of 22)
Items from market group: Ship Equipment > Turrets & Launchers (429 of 889)
Items from market group: Ship Equipment > Turrets & Launchers (444 of 907)
Module: Interdiction Sphere Launcher I
"""
@@ -9344,9 +9313,9 @@ class Effect3029(BaseEffect):
overloadSelfEmHardeningBonus
Used by:
Modules named like: Anti EM Shield Hardener (21 of 21)
Variations of module: Anti-EM Shield Hardener I (20 of 20)
Variations of module: Armor EM Hardener I (39 of 39)
Variations of module: EM Armor Hardener I (37 of 37)
Variations of module: EM Shield Hardener I (20 of 20)
Module: Civilian EM Shield Hardener
"""
type = 'overheat'
@@ -9361,9 +9330,9 @@ class Effect3030(BaseEffect):
overloadSelfThermalHardeningBonus
Used by:
Variations of module: Anti-Thermal Shield Hardener I (20 of 20)
Variations of module: Armor Thermal Hardener I (39 of 39)
Module: Civilian Anti-Thermal Shield Hardener
Variations of module: Thermal Armor Hardener I (37 of 37)
Variations of module: Thermal Shield Hardener I (20 of 20)
Module: Civilian Thermal Shield Hardener
"""
type = 'overheat'
@@ -9378,9 +9347,9 @@ class Effect3031(BaseEffect):
overloadSelfExplosiveHardeningBonus
Used by:
Variations of module: Anti-Explosive Shield Hardener I (20 of 20)
Variations of module: Armor Explosive Hardener I (39 of 39)
Module: Civilian Anti-Explosive Shield Hardener
Variations of module: Explosive Armor Hardener I (37 of 37)
Variations of module: Explosive Shield Hardener I (20 of 20)
Module: Civilian Explosive Shield Hardener
"""
type = 'overheat'
@@ -9395,9 +9364,9 @@ class Effect3032(BaseEffect):
overloadSelfKineticHardeningBonus
Used by:
Modules named like: Anti Kinetic Shield Hardener (21 of 21)
Variations of module: Anti-Kinetic Shield Hardener I (20 of 20)
Variations of module: Armor Kinetic Hardener I (39 of 39)
Variations of module: Kinetic Armor Hardener I (37 of 37)
Variations of module: Kinetic Shield Hardener I (20 of 20)
Module: Civilian Kinetic Shield Hardener
"""
type = 'overheat'
@@ -9413,7 +9382,7 @@ class Effect3035(BaseEffect):
Used by:
Modules named like: Capital Flex Hardener (9 of 9)
Variations of module: Adaptive Invulnerability Shield Hardener I (18 of 18)
Variations of module: Multispectrum Shield Hardener I (18 of 18)
"""
type = 'overheat'
@@ -16755,7 +16724,7 @@ class Effect4951(BaseEffect):
Used by:
Implants named like: Agency 'Hardshell' TB Dose (4 of 4)
Implants named like: Blue Pill Booster (10 of 10)
Implants named like: Blue Pill Booster (5 of 5)
Implant: Antipharmakon Thureo
"""
@@ -16809,15 +16778,9 @@ class Effect4970(BaseEffect):
boosterShieldBoostAmountPenaltyShieldSkills
Used by:
Implant: Improved Crash Booster
Implant: Improved Frentix Booster
Implant: Improved Mindflood Booster
Implant: Standard Crash Booster
Implant: Standard Frentix Booster
Implant: Standard Mindflood Booster
Implant: Strong Crash Booster
Implant: Strong Frentix Booster
Implant: Strong Mindflood Booster
Implants named like: Crash Booster (3 of 4)
Implants named like: Frentix Booster (3 of 4)
Implants named like: Mindflood Booster (3 of 4)
"""
attr = 'boosterShieldBoostAmountPenalty'
@@ -16919,7 +16882,7 @@ class Effect4989(BaseEffect):
missileSkillAoeCloudSizeBonusAllIncludingCapitals
Used by:
Implants named like: Crash Booster (9 of 9)
Implants named like: Crash Booster (4 of 4)
"""
type = 'passive'
@@ -18295,7 +18258,7 @@ class Effect5201(BaseEffect):
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
level = container.level
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Armor Reinforcer',
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Armor Plate',
'massAddition', container.getModifiedItemAttr('massPenaltyReduction') * level, **kwargs)
@@ -18696,7 +18659,7 @@ class Effect5231(BaseEffect):
modifyActiveArmorResonancePostPercent
Used by:
Modules from group: Armor Hardener (156 of 156)
Modules from group: Armor Hardener (148 of 148)
Modules from group: Flex Armor Hardener (4 of 4)
"""
@@ -19844,7 +19807,7 @@ class Effect5364(BaseEffect):
Used by:
Implants named like: Agency 'Hardshell' TB Dose (4 of 4)
Implants named like: Exile Booster (9 of 9)
Implants named like: Exile Booster (4 of 4)
Implant: Antipharmakon Kosybo
"""
@@ -31333,7 +31296,6 @@ class Effect6713(BaseEffect):
shipBonusSupercarrierM1BurstProjectorWebBonus
Used by:
Ship: Hel
Ship: Vendetta
"""
@@ -36395,3 +36357,349 @@ class Effect8026(BaseEffect):
fit.modules.filteredChargeBoost(
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
'aoeVelocity', implant.getModifiedItemAttr('hydraMissileExplosionVelocityBonus'), **kwargs)
class Effect8029(BaseEffect):
"""
roleBonus7CapBoosterGroupRestriction
Used by:
Ships from group: Force Auxiliary (6 of 6)
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
for attr in ('maxGroupOnline', 'maxGroupFitted'):
fit.modules.filteredItemForce(
lambda mod: mod.item.group.name == 'Capacitor Booster',
attr, ship.getModifiedItemAttr('shipBonusRole7'), **kwargs)
class Effect8034(BaseEffect):
"""
smallUpwellWeaponDmgBonusRequiredSkill
Used by:
Skill: Small Vorton Projector
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Vorton Projector'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8035(BaseEffect):
"""
mediumUpwellWeaponDmgBonusRequiredSkill
Used by:
Skill: Medium Vorton Projector
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Medium Vorton Projector'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8036(BaseEffect):
"""
largeUpwellWeaponDmgBonusRequiredSkill
Used by:
Skill: Large Vorton Projector
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Large Vorton Projector'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8037(BaseEffect):
"""
ChainLightning
Used by:
Modules from group: Vorton Projector (15 of 15)
"""
type = 'active'
class Effect8039(BaseEffect):
"""
upwellSkillaoeVelocityaoeCloudSizeBonus
Used by:
Skill: Vorton Arc Guidance
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Vorton Projector Operation'),
'aoeVelocity', skill.getModifiedItemAttr('aoeVelocityBonus') * skill.level, **kwargs)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Vorton Projector Operation'),
'aoeCloudSize', skill.getModifiedItemAttr('aoeCloudSizeBonus') * skill.level, **kwargs)
class Effect8041(BaseEffect):
"""
upwellSkillDamageMuliplierBonus
Used by:
Skill: Vorton Power Amplification
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Vorton Projector',
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8042(BaseEffect):
"""
upwellSkillSpeedBonus
Used by:
Skill: Vorton Projector Operation
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Vorton Projector Operation'),
'speed', skill.getModifiedItemAttr('turretSpeeBonus') * skill.level, **kwargs)
class Effect8044(BaseEffect):
"""
smallVortonProjectorSkillDmgBonus
Used by:
Skill: Small Vorton Specialization
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Vorton Specialization'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8045(BaseEffect):
"""
mediumVortonProjectorSkillDmgBonus
Used by:
Skill: Medium Vorton Specialization
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Medium Vorton Specialization'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8046(BaseEffect):
"""
largeVortonProjectorSkillDmgBonus
Used by:
Skill: Large Vorton Specialization
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Large Vorton Specialization'),
'damageMultiplier', skill.getModifiedItemAttr('damageMultiplierBonus') * skill.level, **kwargs)
class Effect8047(BaseEffect):
"""
shipBonusUF1shieldResistance
Used by:
Ship: Skybreaker
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
for type in ('kinetic', 'thermal', 'explosive', 'em'):
fit.ship.boostItemAttr('shield%sDamageResonance' % type.capitalize(),
ship.getModifiedItemAttr('shipBonusUF1'),
skill='EDENCOM Frigate', **kwargs)
class Effect8048(BaseEffect):
"""
shipBonusUF2damage
Used by:
Ship: Skybreaker
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Small Vorton Projector'), 'damageMultiplier',
ship.getModifiedItemAttr('shipBonusUF2'), skill='EDENCOM Frigate', **kwargs)
class Effect8052(BaseEffect):
"""
shipBonusUC2ShieldResistance
Used by:
Ship: Stormbringer
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
for type in ('kinetic', 'thermal', 'explosive', 'em'):
fit.ship.boostItemAttr('shield%sDamageResonance' % type.capitalize(),
ship.getModifiedItemAttr('shipBonusUC2'),
skill='EDENCOM Cruiser', **kwargs)
class Effect8053(BaseEffect):
"""
shipBonusUC1maxRange
Used by:
Ship: Stormbringer
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Vorton Projector'), 'maxRange',
ship.getModifiedItemAttr('shipBonusUC1'), skill='EDENCOM Cruiser', **kwargs)
class Effect8054(BaseEffect):
"""
shipBonusUB1upwellDamage
Used by:
Ship: Thunderchild
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Large Vorton Projector'), 'damageMultiplier',
ship.getModifiedItemAttr('shipBonusUB1'), skill='EDENCOM Battleship', **kwargs)
class Effect8056(BaseEffect):
"""
shipBonusUB2upwellROF
Used by:
Ship: Thunderchild
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Large Vorton Projector'), 'speed',
ship.getModifiedItemAttr('shipBonusUB2'), skill='EDENCOM Battleship', **kwargs)
class Effect8057(BaseEffect):
"""
vortonWeaponDamageSpeedMultiply
Used by:
Modules from group: Vorton Projector Upgrade (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == 'Vorton Projector',
'damageMultiplier', module.getModifiedItemAttr('damageMultiplier'),
stackingPenalties=True, **kwargs)
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == 'Vorton Projector',
'speed', module.getModifiedItemAttr('speedMultiplier'),
stackingPenalties=True, **kwargs)
class Effect8062(BaseEffect):
"""
ammoAOEvelocityMultiplier
Used by:
Charges from group: Advanced Condensor Pack (6 of 6)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
module.multiplyItemAttr('aoeVelocity', module.getModifiedChargeAttr('aoeVelocityBonus') or 0, **kwargs)
class Effect8064(BaseEffect):
"""
vortonProjectorOptimalRangeBonus
Used by:
Implants named like: EDENCOM Vorton Booster RA (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, implant, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Vorton Projector Operation'),
'maxRange', implant.getModifiedItemAttr('rangeSkillBonus'), **kwargs)
class Effect8065(BaseEffect):
"""
vortonProjectorSkillRangeBonus
Used by:
Skill: Vorton Extension
"""
type = 'passive'
@staticmethod
def handler(fit, skill, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Vorton Projector Operation'),
'maxRange', skill.getModifiedItemAttr('rangeSkillBonus') * skill.level, **kwargs)

View File

@@ -209,40 +209,13 @@ class Effect(EqBase):
class Item(EqBase):
MOVE_ATTRS = (4, # Mass
38, # Capacity
161) # Volume
MOVE_ATTR_INFO = None
ABYSSAL_TYPES = None
@classmethod
def getMoveAttrInfo(cls):
info = getattr(cls, "MOVE_ATTR_INFO", None)
if info is None:
cls.MOVE_ATTR_INFO = info = []
for id in cls.MOVE_ATTRS:
info.append(eos.db.getAttributeInfo(id))
return info
def moveAttrs(self):
self.__moved = True
for info in self.getMoveAttrInfo():
val = getattr(self, info.name, 0)
if val != 0:
attr = Attribute()
attr.info = info
attr.value = val
self.__attributes[info.name] = attr
@reconstructor
def init(self):
self.__race = None
self.__requiredSkills = None
self.__requiredFor = None
self.__moved = False
self.__offensive = None
self.__assistive = None
self.__overrides = None
@@ -264,9 +237,6 @@ class Item(EqBase):
@property
def attributes(self):
if not self.__moved:
self.moveAttrs()
return self.__attributes
@property

View File

@@ -21,6 +21,9 @@ from logbook import Logger
from sqlalchemy.orm import reconstructor
from eos.utils.round import roundToPrec
pyfalog = Logger(__name__)
@@ -56,9 +59,8 @@ class BoosterSideEffect:
@property
def name(self):
return "{0}% {1}".format(
self.booster.getModifiedItemAttr(self.attr),
self.__effect.getattr('displayName') or self.__effect.name,
)
roundToPrec(self.booster.getModifiedItemAttr(self.attr), 5),
self.__effect.getattr('displayName') or self.__effect.name)
@property
def attr(self):

View File

@@ -1026,6 +1026,16 @@ class Fit:
if mod.isEmpty:
del self.modules[i]
def clearTail(self):
tailPositions = {}
for mod in reversed(self.modules):
if not mod.isEmpty:
break
tailPositions[self.modules.index(mod)] = mod.slot
for pos in sorted(tailPositions, reverse=True):
self.modules.remove(self.modules[pos])
return tailPositions
@property
def modCount(self):
x = 0
@@ -1135,7 +1145,7 @@ class Fit:
def droneBayUsed(self):
amount = 0
for d in self.drones:
amount += d.item.volume * d.amount
amount += d.item.attributes['volume'].value * d.amount
return amount
@@ -1143,7 +1153,7 @@ class Fit:
def fighterBayUsed(self):
amount = 0
for f in self.fighters:
amount += f.item.volume * f.amount
amount += f.item.attributes['volume'].value * f.amount
return amount

View File

@@ -214,8 +214,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if charge is None:
charges = 0
else:
chargeVolume = charge.volume
containerCapacity = self.item.capacity
chargeVolume = charge.attributes['volume'].value
containerCapacity = self.item.attributes['capacity'].value
if chargeVolume is None or containerCapacity is None:
charges = 0
else:
@@ -696,7 +696,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check this only if we're told to do so
if hardpointLimit:
if fit.getHardpointsFree(self.hardpoint) < 1:
if fit.getHardpointsFree(self.hardpoint) < (1 if self.owner != fit else 0):
return False
return True
@@ -778,8 +778,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check sizes, if 'charge size > module volume' it won't fit
if charge is None:
return True
chargeVolume = charge.volume
moduleCapacity = self.item.capacity
chargeVolume = charge.attributes['volume'].value
moduleCapacity = self.item.attributes['capacity'].value
if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity:
return False

View File

@@ -81,6 +81,8 @@ class Mutator(EqBase):
@validates("value")
def validator(self, key, val):
""" Validates values as properly falling within the range of the modules' Mutaplasmid """
if self.baseValue == 0:
return 0
mod = val / self.baseValue
if self.minMod <= mod <= self.maxMod:

27
eos/utils/round.py Normal file
View File

@@ -0,0 +1,27 @@
import math
def roundToPrec(val, prec, nsValue=None):
"""
nsValue: custom value which should be used to determine normalization shift
"""
# We're not rounding integers anyway
# Also make sure that we do not ask to calculate logarithm of zero
if int(val) == val:
return int(val)
roundFactor = int(prec - math.floor(math.log10(abs(val if nsValue is None else nsValue))) - 1)
# But we don't want to round integers
if roundFactor < 0:
roundFactor = 0
# Do actual rounding
val = round(val, roundFactor)
# Make sure numbers with .0 part designating float don't get through
if int(val) == val:
val = int(val)
return val
def roundDec(val, prec):
if int(val) == val:
return int(val)
return round(val, prec)

View File

@@ -37,7 +37,14 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
for mod in src.item.activeModulesIter():
if not mod.isDealingDamage():
continue
if mod.hardpoint == FittingHardpoint.TURRET:
if "ChainLightning" in mod.item.effects:
if inLockRange:
applicationMap[mod] = getVortonMult(
mod=mod,
distance=distance,
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
elif mod.hardpoint == FittingHardpoint.TURRET:
if inLockRange:
applicationMap[mod] = getTurretMult(
mod=mod,
@@ -56,7 +63,6 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
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)
@@ -151,7 +157,21 @@ def getTurretMult(mod, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngl
return mult
def getLauncherMult(mod, src, distance, tgtSpeed, tgtSigRadius):
def getVortonMult(mod, distance, tgtSpeed, tgtSigRadius):
rangeFactor = calculateRangeFactor(
mod.getModifiedItemAttr('maxRange'),
0,
distance)
applicationFactor = _calcMissileFactor(
atkEr=mod.getModifiedItemAttr('aoeCloudSize'),
atkEv=mod.getModifiedItemAttr('aoeVelocity'),
atkDrf=mod.getModifiedItemAttr('aoeDamageReductionFactor'),
tgtSpeed=tgtSpeed,
tgtSigRadius=tgtSigRadius)
return rangeFactor * applicationFactor
def getLauncherMult(mod, distance, tgtSpeed, tgtSigRadius):
missileMaxRangeData = mod.missileMaxRangeData
if missileMaxRangeData is None:
return 0

View File

@@ -235,7 +235,7 @@ class GraphControlPanel(wx.Panel):
fieldTextBox = FloatBox(self, self._storedConsts.get((inputDef.handle, inputDef.unit), inputDef.defaultValue))
fieldTextBox.Bind(wx.EVT_TEXT, self.OnNonMainInputChanged)
fieldTextBox.SetToolTip(wx.ToolTip(tooltipText))
fieldSizer.Add(fieldTextBox, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
fieldSizer.Add(fieldTextBox, 0, wx.EXPAND | wx.RIGHT, 5)
fieldIcon = None
if inputDef.iconID is not None:
icon = BitmapLoader.getBitmap(inputDef.iconID, 'icons')

View File

@@ -27,7 +27,9 @@ class AuxiliaryFrame(wx.Frame):
_instance = None
def __init__(self, parent, id=None, title=None, pos=None, size=None, style=None, name=None, resizeable=False):
baseStyle = wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU
baseStyle = wx.FRAME_NO_TASKBAR | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU
if parent is not None:
baseStyle = baseStyle | wx.FRAME_FLOAT_ON_PARENT
if resizeable:
baseStyle = baseStyle | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX
kwargs = {

View File

@@ -24,6 +24,7 @@ import gui.display as d
import gui.fitCommands as cmd
import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
from gui.builtinMarketBrowser.events import ITEM_SELECTED, ItemSelected
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
@@ -58,6 +59,7 @@ class CargoView(d.Display):
self.lastFitId = None
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
@@ -66,6 +68,31 @@ class CargoView(d.Display):
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
def addItem(self, event):
item = Market.getInstance().getItem(event.itemID, eager='group')
if item is None or not item.isCharge:
event.Skip()
return
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if not fit:
event.Skip()
return
modifiers = wx.GetMouseState().GetModifiers()
amount = 1
if modifiers == wx.MOD_CONTROL:
amount = 10
elif modifiers == wx.MOD_ALT:
amount = 100
elif modifiers == wx.MOD_CONTROL | wx.MOD_ALT:
amount = 1000
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(
fitID=fitID, itemID=item.ID, amount=amount))
self.mainFrame.additionsPane.select('Cargo')
event.Skip()
def handleListDrag(self, x, y, data):
"""
Handles dragging of items from various pyfa displays which support it

View File

@@ -126,10 +126,13 @@ class AttributeSlider(wx.Panel):
def SetValue(self, value, post_event=True, affect_modified_flag=True):
self.ctrl.SetValue(value)
invert_factor = -1 if self.inverse else 1
if value >= self.base_value:
slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor
else:
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
try:
if value >= self.base_value:
slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor
else:
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
except ZeroDivisionError:
slider_percentage = 0
self.slider.SetValue(slider_percentage)
if post_event:
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage, affect_modified_flag=affect_modified_flag))

View File

@@ -93,6 +93,8 @@ class ItemMutatorList(wx.ScrolledWindow):
first = True
for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
if m.baseValue == 0:
continue
if not first:
sizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
first = False

View File

@@ -1,15 +1,16 @@
import wx
from logbook import Logger
from eos.saveddata.module import Module
import gui.builtinMarketBrowser.pfSearchBox as SBox
from config import slotColourMap
from eos.saveddata.module import Module
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES
from gui.contextMenu import ContextMenu
from gui.display import Display
from gui.utils.staticHelpers import DragDropHelper
from service.attribute import Attribute
from service.fit import Fit
from config import slotColourMap
from service.market import Market
pyfalog = Logger(__name__)
@@ -170,8 +171,8 @@ class ItemView(Display):
def scheduleSearch(self, event=None):
self.searchTimer.Stop() # Cancel any pending timers
search = self.marketBrowser.search.GetLineText(0)
# Make sure we do not count wildcard as search symbol
realsearch = search.replace("*", "")
# Make sure we do not count wildcards as search symbol
realsearch = search.replace('*', '').replace('?', '')
# Re-select market group if search query has zero length
if len(realsearch) == 0:
self.selectionMade('search')
@@ -193,10 +194,11 @@ class ItemView(Display):
self.setToggles()
self.filterItemStore()
def populateSearch(self, items):
def populateSearch(self, itemIDs):
# If we're no longer searching, dump the results
if self.marketBrowser.mode != 'search':
return
items = Market.getItems(itemIDs)
self.updateItemStore(items)
self.setToggles()
self.filterItemStore()

View File

@@ -60,7 +60,8 @@ class MarketTree(wx.TreeCtrl):
# If market should have items but it doesn't, do not show it
if sMkt.marketGroupValidityCheck(childMktGrp) is False:
continue
iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp))
icon = sMkt.getIconByMarketGroup(childMktGrp)
iconId = -1 if icon is None else self.addImage(icon)
try:
childId = self.AppendItem(root, childMktGrp.name, iconId, data=childMktGrp.ID)
except (KeyboardInterrupt, SystemExit):

View File

@@ -35,31 +35,31 @@ class PFGeneralPref(PreferenceView):
# Database path
self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, "pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stSetUserPath.Wrap(-1)
mainSizer.Add(self.stSetUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stSetUserPath, 0, wx.ALL, 5)
self.inputUserPath = wx.TextCtrl(panel, wx.ID_ANY, config.savePath, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputUserPath.SetEditable(False)
self.inputUserPath.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.EXPAND, 5)
# Save DB
self.stFitDB = wx.StaticText(panel, wx.ID_ANY, "Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stFitDB.Wrap(-1)
mainSizer.Add(self.stFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stFitDB, 0, wx.ALL, 5)
self.inputFitDB = wx.TextCtrl(panel, wx.ID_ANY, config.saveDB, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputFitDB.SetEditable(False)
self.inputFitDB.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.EXPAND, 5)
# Game Data DB
self.stGameDB = wx.StaticText(panel, wx.ID_ANY, "Game Database:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stGameDB.Wrap(-1)
mainSizer.Add(self.stGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stGameDB, 0, wx.ALL, 5)
self.inputGameDB = wx.TextCtrl(panel, wx.ID_ANY, config.gameDB, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputGameDB.SetEditable(False)
self.inputGameDB.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
mainSizer.Add(self.inputGameDB, 0, wx.ALL | wx.EXPAND, 5)
self.cbsaveInRoot.SetValue(config.saveInRoot)
self.cbsaveInRoot.Bind(wx.EVT_CHECKBOX, self.onCBsaveInRoot)

View File

@@ -50,7 +50,7 @@ class PFHTMLExportPref(PreferenceView):
self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0, 0))
self.fileSelectButton.Bind(wx.EVT_BUTTON, self.selectHTMLExportFilePath)
mainSizer.Add(self.fileSelectButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.fileSelectButton, 0, wx.ALL, 5)
self.stDesc4 = wx.StaticText(panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0)
self.stDesc4.Wrap(dlgWidth - 50)

View File

@@ -36,20 +36,20 @@ class PFGeneralPref(PreferenceView):
# Database path
self.stLogPath = wx.StaticText(panel, wx.ID_ANY, "Log file location:", wx.DefaultPosition, wx.DefaultSize, 0)
self.stLogPath.Wrap(-1)
mainSizer.Add(self.stLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stLogPath, 0, wx.ALL, 5)
self.inputLogPath = wx.TextCtrl(panel, wx.ID_ANY, config.logPath, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputLogPath.SetEditable(False)
self.inputLogPath.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.EXPAND, 5)
import requests
self.certPath = wx.StaticText(panel, wx.ID_ANY, "Cert Path:", wx.DefaultPosition, wx.DefaultSize, 0)
self.certPath .Wrap(-1)
mainSizer.Add(self.certPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.certPath, 0, wx.ALL, 5)
self.certPathCtrl = wx.TextCtrl(panel, wx.ID_ANY, requests.certs.where(), wx.DefaultPosition, wx.DefaultSize, 0)
self.certPathCtrl.SetEditable(False)
self.certPathCtrl.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.EXPAND, 5)
# Debug Logging
self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0)
@@ -57,7 +57,7 @@ class PFGeneralPref(PreferenceView):
self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, "Pressing this button will cause all logs in memory to write to the log file:",
wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.stDumpLogs, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stDumpLogs, 0, wx.ALL, 5)
self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, "Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0)
self.btnDumpLogs.Bind(wx.EVT_BUTTON, OnDumpLogs)
mainSizer.Add(self.btnDumpLogs, 0, wx.ALIGN_LEFT, 5)

View File

@@ -135,7 +135,7 @@ class PFNetworkPref(PreferenceView):
self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize,
0)
self.stPSAutoDetected.Wrap(-1)
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL, 5)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.AddStretchSpacer()

View File

@@ -86,8 +86,8 @@ class NavigationPanel(SFItem.SFBrowserItem):
def OnScheduleSearch(self, event):
search = self.BrowserSearchBox.GetValue()
# Make sure we do not count wildcard as search symbol
realsearch = search.replace("*", "")
# Make sure we do not count wildcards as search symbol
realsearch = search.replace('*', '').replace('?', '')
minChars = 1 if isStringCjk(realsearch) else 3
if len(realsearch) >= minChars:
self.lastSearch = search

View File

@@ -51,8 +51,7 @@ class FirepowerViewFull(StatsView):
self.headerPanel = headerPanel
hsizer = self.headerPanel.Parent.GetHeaderContentSizer()
self.stEff = wx.StaticText(self.headerPanel, wx.ID_ANY, "( Effective )")
hsizer.Add(self.stEff)
# self.headerPanel.GetParent().AddToggleItem(self.stEff)
hsizer.Insert(0, self.stEff)
panel = "full"
@@ -130,9 +129,12 @@ class FirepowerViewFull(StatsView):
self.panel.GetSizer().Layout()
# Remove effective label
hsizer = self.headerPanel.GetSizer()
hsizer.Hide(self.stEff)
# self.stEff.Destroy()
hsizer = self.headerPanel.Parent.GetHeaderContentSizer()
for i, c in enumerate(hsizer.Children):
if c.GetWindow() is self.stEff:
hsizer.Remove(i)
self.stEff.Destroy()
break
# Get the new view
view = StatsView.getView("miningyieldViewFull")(self.parent)

View File

@@ -123,6 +123,15 @@ class Miscellanea(ViewColumn):
text = ' | '.join(i[0] for i in info)
tooltip = ' and '.join(i[1] for i in info).capitalize()
return text, tooltip
elif itemGroup == "Vorton Projector":
cloudSize = stuff.getModifiedItemAttr("aoeCloudSize")
aoeVelocity = stuff.getModifiedItemAttr("aoeVelocity")
if not cloudSize or not aoeVelocity:
return "", None
text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m",
formatAmount(aoeVelocity, 3, 0, 3), "m/s")
tooltip = "Explosion radius and explosion velocity"
return text, tooltip
elif itemCategory == "Subsystem":
slots = ("hi", "med", "low")
info = []
@@ -279,7 +288,8 @@ class Miscellanea(ViewColumn):
"Heat Sink",
"Ballistic Control system",
"Structure Weapon Upgrade",
"Entropic Radiation Sink"
"Entropic Radiation Sink",
"Vorton Projector Upgrade"
):
attrMap = {
"Gyrostabilizer": ("damageMultiplier", "speedMultiplier", "Projectile weapon"),
@@ -287,7 +297,8 @@ class Miscellanea(ViewColumn):
"Heat Sink": ("damageMultiplier", "speedMultiplier", "Energy weapon"),
"Ballistic Control system": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
"Structure Weapon Upgrade": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon")}
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon"),
"Vorton Projector Upgrade": ("damageMultiplier", "speedMultiplier", "Vorton projector")}
dmgAttr, rofAttr, weaponName = attrMap[itemGroup]
dmg = stuff.getModifiedItemAttr(dmgAttr)
rof = stuff.getModifiedItemAttr(rofAttr)

View File

@@ -377,23 +377,7 @@ class FittingView(d.Display):
event.Skip()
return
batchOp = wx.GetMouseState().GetModifiers() == wx.MOD_ALT and getattr(event, 'allowBatch', None) is not False
# If we've selected ammo, then apply to the selected module(s)
if item.isCharge:
positions = []
fit = Fit.getInstance().getFit(fitID)
if batchOp:
for position, mod in enumerate(fit.modules):
if isinstance(mod, Module) and not mod.isEmpty:
positions.append(position)
else:
for mod in self.getSelectedMods():
if mod.isEmpty or mod not in fit.modules:
continue
positions.append(fit.modules.index(mod))
if len(positions) > 0:
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
fitID=fitID, positions=positions, chargeItemID=itemID))
elif (item.isModule and not batchOp) or item.isSubsystem:
if (item.isModule and not batchOp) or item.isSubsystem:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isModule and batchOp:
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))

View File

@@ -302,8 +302,8 @@ class ItemView(d.Display):
sMkt = Market.getInstance()
search = self.searchBox.GetLineText(0)
# Make sure we do not count wildcard as search symbol
realsearch = search.replace("*", "")
# Make sure we do not count wildcards as search symbol
realsearch = search.replace('*', '').replace('?', '')
# Show nothing if query is too short
if len(realsearch) < 3:
self.clearSearch()
@@ -311,12 +311,12 @@ class ItemView(d.Display):
sMkt.searchItems(search, self.populateSearch, 'implants')
def populateSearch(self, items):
def populateSearch(self, itemIDs):
if not self.IsShown():
self.parent.availableImplantsTree.Hide()
self.Show()
self.parent.Layout()
items = Market.getItems(itemIDs)
items = [i for i in items if i.group.name != 'Booster']
self.items = sorted(list(items), key=lambda i: i.name)

View File

@@ -402,7 +402,7 @@ class SkillTreeView(wx.Panel):
setattr(self, "{}Btn".format(name.lower()), btn)
btn.Enable(True)
btn.SetToolTip("%s skills %s clipboard" % (name, direction))
bSizerButtons.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT | wx.ALL, 5)
bSizerButtons.Add(btn, 0, wx.ALL, 5)
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Skills".format(name.lower())))
pmainSizer.Add(bSizerButtons, 0, wx.EXPAND, 5)
@@ -833,7 +833,7 @@ class APIView(wx.Panel):
def fetchSkills(self, evt):
sChar = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity()
sChar.apiFetch(char.ID, self.__fetchCallback)
sChar.apiFetch(char.ID, APIView.fetchCallback)
def addCharacter(self, event):
sEsi = Esi.getInstance()
@@ -899,7 +899,8 @@ class APIView(wx.Panel):
if event is not None:
event.Skip()
def __fetchCallback(self, e=None):
@staticmethod
def fetchCallback(e=None):
if e:
pyfalog.warn("Error fetching skill information for character for __fetchCallback")
exc_type, exc_value, exc_trace = e

View File

@@ -106,7 +106,7 @@ class ErrorFrame(AuxiliaryFrame):
self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version + version_block.strip(), wx.DefaultPosition,
(-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP)
self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5)
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL, 5)
self.errorTextCtrl.AppendText("\n")
self.errorTextCtrl.Layout()

View File

@@ -5,11 +5,14 @@ import requests
import wx
from logbook import Logger
import config
import gui.globalEvents as GE
from eos.db import getItem
from eos.saveddata.cargo import Cargo
from gui.auxFrame import AuxiliaryFrame
from gui.display import Display
from gui.characterEditor import APIView
from service.character import Character
from service.esi import Esi
from service.esiAccess import APIException
from service.fit import Fit
@@ -335,7 +338,7 @@ class SsoCharacterMgmt(AuxiliaryFrame):
self.addBtn = wx.Button(self, wx.ID_ANY, "Add Character", wx.DefaultPosition, wx.DefaultSize, 0)
btnSizer.Add(self.addBtn, 0, wx.ALL | wx.EXPAND, 5)
self.deleteBtn = wx.Button(self, wx.ID_ANY, "Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0)
self.deleteBtn = wx.Button(self, wx.ID_ANY, "Remove Character", wx.DefaultPosition, wx.DefaultSize, 0)
btnSizer.Add(self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5)
mainSizer.Add(btnSizer, 0, wx.EXPAND, 5)
@@ -355,6 +358,16 @@ class SsoCharacterMgmt(AuxiliaryFrame):
def ssoLogin(self, event):
self.popCharList()
sChar = Character.getInstance()
# Update existing pyfa character, if it doesn't exist - create new
char = sChar.getCharacter(event.character.characterName)
newChar = False
if char is None:
char = sChar.new(event.character.characterName)
newChar = True
char.setSsoCharacter(event.character, config.getClientSecret())
sChar.apiFetch(char.ID, APIView.fetchCallback)
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
event.Skip()
def kbEvent(self, event):

View File

@@ -40,9 +40,6 @@ class CalcAddLocalModuleCommand(wx.Command):
position=fit.modules.index(oldMod),
newModInfo=self.newModInfo)
return self.subsystemCmd.Do()
if not newMod.fits(fit):
pyfalog.warning('Module does not fit')
return False
fit.modules.append(newMod)
if newMod not in fit.modules:
pyfalog.warning('Failed to append to list')
@@ -52,6 +49,13 @@ class CalcAddLocalModuleCommand(wx.Command):
# relationship via .owner attribute, which is handled by SQLAlchemy
eos.db.flush()
sFit.recalc(fit)
# fits() sometimes relies on recalculated on-item attributes, such as fax cap
# booster limitation, so we have to check it after recalculating and remove the
# module if the check has failed
if not newMod.fits(fit):
pyfalog.warning('Module does not fit')
self.Undo()
return False
self.savedStateCheckChanges = sFit.checkStates(fit, newMod)
return True
@@ -63,7 +67,7 @@ class CalcAddLocalModuleCommand(wx.Command):
if self.savedPosition is None:
return False
from .localRemove import CalcRemoveLocalModulesCommand
cmd = CalcRemoveLocalModulesCommand(fitID=self.fitID, positions=[self.savedPosition], recalc=False)
cmd = CalcRemoveLocalModulesCommand(fitID=self.fitID, positions=[self.savedPosition], recalc=False, clearTail=True)
if not cmd.Do():
return False
restoreCheckedStates(Fit.getInstance().getFit(self.fitID), self.savedStateCheckChanges)

View File

@@ -3,7 +3,7 @@ from logbook import Logger
import eos.db
from eos.const import FittingSlot
from gui.fitCommands.helpers import ModuleInfo, restoreCheckedStates
from gui.fitCommands.helpers import ModuleInfo, restoreCheckedStates, restoreRemovedDummies
from service.fit import Fit
@@ -12,14 +12,16 @@ pyfalog = Logger(__name__)
class CalcRemoveLocalModulesCommand(wx.Command):
def __init__(self, fitID, positions, recalc=True):
def __init__(self, fitID, positions, recalc=True, clearTail=False):
wx.Command.__init__(self, True, 'Remove Module')
self.fitID = fitID
self.positions = positions
self.recalc = recalc
self.clearTail = clearTail
self.savedSubInfos = None
self.savedModInfos = None
self.savedStateCheckChanges = None
self.savedTail = None
def Do(self):
pyfalog.debug('Doing removal of local modules from positions {} on fit {}'.format(self.positions, self.fitID))
@@ -40,6 +42,9 @@ class CalcRemoveLocalModulesCommand(wx.Command):
if len(self.savedSubInfos) == 0 and len(self.savedModInfos) == 0:
return False
if self.clearTail:
self.savedTail = fit.clearTail()
if self.recalc:
# Need to flush because checkStates sometimes relies on module->fit
# relationship via .owner attribute, which is handled by SQLAlchemy
@@ -76,6 +81,7 @@ class CalcRemoveLocalModulesCommand(wx.Command):
if not any(results):
return False
restoreCheckedStates(fit, self.savedStateCheckChanges)
restoreRemovedDummies(fit, self.savedTail)
return True
@property

View File

@@ -39,7 +39,17 @@ class CalcReplaceLocalModuleCommand(wx.Command):
if newMod.slot != oldMod.slot:
return False
# Dummy it out in case the next bit fails
fit.modules.free(self.position)
fit.modules.replace(self.position, newMod)
if newMod not in fit.modules:
pyfalog.warning('Failed to replace in list')
self.Undo()
return False
if self.recalc:
# Need to flush because checkStates sometimes relies on module->fit
# relationship via .owner attribute, which is handled by SQLAlchemy
eos.db.flush()
sFit.recalc(fit)
self.savedStateCheckChanges = sFit.checkStates(fit, newMod)
if not self.ignoreRestrictions and not newMod.fits(fit):
pyfalog.warning('Module does not fit')
self.Undo()
@@ -52,17 +62,6 @@ class CalcReplaceLocalModuleCommand(wx.Command):
pyfalog.warning('Invalid charge')
self.Undo()
return False
fit.modules.replace(self.position, newMod)
if newMod not in fit.modules:
pyfalog.warning('Failed to replace in list')
self.Undo()
return False
if self.recalc:
# Need to flush because checkStates sometimes relies on module->fit
# relationship via .owner attribute, which is handled by SQLAlchemy
eos.db.flush()
sFit.recalc(fit)
self.savedStateCheckChanges = sFit.checkStates(fit, newMod)
return True
def Undo(self):

View File

@@ -353,6 +353,8 @@ def restoreCheckedStates(fit, stateInfo, ignoreModPoss=()):
def restoreRemovedDummies(fit, dummyInfo):
if dummyInfo is None:
return
# Need this to properly undo the case when removal of subsystems removes dummy slots
for position in sorted(dummyInfo):
slot = dummyInfo[position]

View File

@@ -80,8 +80,8 @@ class MainMenuBar(wx.MenuBar):
fitMenu = wx.Menu()
self.Append(fitMenu, "Fi&t")
fitMenu.Append(wx.ID_UNDO)
fitMenu.Append(wx.ID_REDO)
fitMenu.Append(wx.ID_UNDO, "&Undo\tCTRL+Z", "Undo the most recent action")
fitMenu.Append(wx.ID_REDO, "&Redo\tCTRL+Y", "Redo the most recent undone action")
fitMenu.AppendSeparator()
fitMenu.Append(wx.ID_COPY, "&To Clipboard\tCTRL+C", "Export a fit to the clipboard")

View File

@@ -181,7 +181,7 @@ class DmgPatternEditor(AuxiliaryFrame):
setattr(self, name, btn)
btn.Enable(True)
btn.SetToolTip("%s patterns %s clipboard" % (name, direction))
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
footerSizer.Add(btn, 0)
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower())))
if not self.entityEditor.checkEntitiesExist():

View File

@@ -13,6 +13,7 @@ from eos.db.gamedata.queries import getAttributeInfo, getItem
from gui.auxFrame import AuxiliaryFrame
from gui.bitmap_loader import BitmapLoader
from gui.marketBrowser import SearchBox
from service.fit import Fit
from service.market import Market
@@ -170,12 +171,15 @@ class ItemView(d.Display):
d.Display.__init__(self, parent)
self.activeItems = []
self.searchTimer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.scheduleSearch, self.searchTimer)
self.searchBox = parent.Parent.Parent.searchBox
# Bind search actions
self.searchBox.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch)
self.searchBox.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch)
self.searchBox.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch)
self.searchBox.Bind(SBox.EVT_TEXT, self.scheduleSearch)
self.searchBox.Bind(SBox.EVT_TEXT, self.delaySearch)
self.update(Market.getInstance().getItemsWithOverrides())
@@ -188,12 +192,17 @@ class ItemView(d.Display):
if updateDisplay:
self.update(Market.getInstance().getItemsWithOverrides())
def delaySearch(self, evt):
sFit = Fit.getInstance()
self.searchTimer.Stop()
self.searchTimer.Start(sFit.serviceFittingOptions["marketSearchDelay"], True)
def scheduleSearch(self, event=None):
sMkt = Market.getInstance()
search = self.searchBox.GetLineText(0)
# Make sure we do not count wildcard as search symbol
realsearch = search.replace("*", "")
# Make sure we do not count wildcards as search symbol
realsearch = search.replace('*', '').replace('?', '')
# Show nothing if query is too short
if len(realsearch) < 3:
self.clearSearch()
@@ -218,7 +227,8 @@ class ItemView(d.Display):
return not isFittable, catname, mktgrpid, parentname, metatab, metalvl, item.name
def populateSearch(self, items):
def populateSearch(self, itemIDs):
items = Market.getItems(itemIDs)
self.update(items)
def populate(self, items):

View File

@@ -254,7 +254,6 @@ class PyGauge(wx.Window):
w = rect.width
else:
w = rect.width * (float(value) / 100)
r = copy.copy(rect)
r.width = w
dc.DrawRectangle(r)
@@ -315,7 +314,8 @@ class PyGauge(wx.Window):
color,
gradient_color
)
dc.DrawBitmap(gradient_bitmap, r.left, r.top)
if gradient_bitmap is not None:
dc.DrawBitmap(gradient_bitmap, r.left, r.top)
# font stuff begins here
dc.SetFont(self.font)

View File

@@ -159,7 +159,7 @@ class ImplantSetEditor(AuxiliaryFrame):
setattr(self, name, btn)
btn.Enable(True)
btn.SetToolTip("%s implant sets %s clipboard" % (name, direction))
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
footerSizer.Add(btn, 0)
mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5)

View File

@@ -148,7 +148,7 @@ class StatsPane(wx.Panel):
sizer.AddStretchSpacer()
# Add menu
header_menu = wx.StaticText(tp.GetHeaderPanel(), wx.ID_ANY, "\u2630", size=wx.Size((10, -1)))
sizer.Add(header_menu , 0, wx.EXPAND | wx.RIGHT | wx.ALIGN_RIGHT, 5)
sizer.Add(header_menu , 0, wx.EXPAND | wx.RIGHT, 5)
header_menu.Bind(wx.EVT_CONTEXT_MENU, handler)
header_menu.Bind(wx.EVT_LEFT_UP, handler)

View File

@@ -183,7 +183,7 @@ class TargetProfileEditor(AuxiliaryFrame):
ttText, unitText = self.ATTRIBUTES[attr]
bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % attr, "gui"))
bmp.SetToolTip(wx.ToolTip(ttText))
miscAttrSizer.Add(bmp, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, leftPad)
miscAttrSizer.Add(bmp, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, leftPad)
# set text edit
editBox = FloatBox(parent=self, id=wx.ID_ANY, value=None, pos=wx.DefaultPosition, size=defSize)
editBox.SetToolTip(wx.ToolTip(ttText))
@@ -231,7 +231,7 @@ class TargetProfileEditor(AuxiliaryFrame):
setattr(self, name, btn)
btn.Enable(True)
btn.SetToolTip("%s profiles %s clipboard" % (name, direction))
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
footerSizer.Add(btn, 0)
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower())))
if not self.entityEditor.checkEntitiesExist():

View File

@@ -35,6 +35,8 @@ def DrawFilledBitmap(width, height, color):
def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4):
if width == 0 or height == 0:
return None
canvas = wx.Bitmap(width, height)
mdc = wx.MemoryDC()

View File

@@ -1,5 +1,7 @@
import math
from eos.utils.round import roundToPrec, roundDec
def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=False, unitName=None):
"""
@@ -97,29 +99,3 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal
else:
result = "{}{} {}{}".format(sign, mantissa, suffix, unitName)
return result
def roundToPrec(val, prec, nsValue=None):
"""
nsValue: custom value which should be used to determine normalization shift
"""
# We're not rounding integers anyway
# Also make sure that we do not ask to calculate logarithm of zero
if int(val) == val:
return int(val)
roundFactor = int(prec - math.floor(math.log10(abs(val if nsValue is None else nsValue))) - 1)
# But we don't want to round integers
if roundFactor < 0:
roundFactor = 0
# Do actual rounding
val = round(val, roundFactor)
# Make sure numbers with .0 part designating float don't get through
if int(val) == val:
val = int(val)
return val
def roundDec(val, prec):
if int(val) == val:
return int(val)
return round(val, prec)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 605 B

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 834 B

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 944 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 931 B

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 851 B

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 978 B

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1002 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 814 B

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