Compare commits
76 Commits
v2.19.1dev
...
v2.21.1dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9c41c6439 | ||
|
|
b24ceb95f5 | ||
|
|
6ba6e0f69b | ||
|
|
9aca49e539 | ||
|
|
01f1cdb175 | ||
|
|
c1356906bb | ||
|
|
8229537e5f | ||
|
|
6cfa2161ec | ||
|
|
fa3cf46786 | ||
|
|
ac40dfd018 | ||
|
|
4c32d559bb | ||
|
|
02ee12586f | ||
|
|
496f15a83e | ||
|
|
acf6af8f95 | ||
|
|
225b00b9ff | ||
|
|
7f2b7415ea | ||
|
|
d1d9ae4dac | ||
|
|
ac7ccfb6a3 | ||
|
|
05c2c41762 | ||
|
|
10f5133282 | ||
|
|
42658a8167 | ||
|
|
6dbf38dbb4 | ||
|
|
64306e2366 | ||
|
|
f259df85cd | ||
|
|
1090589cb2 | ||
|
|
29e09fcdda | ||
|
|
36fd4aeaff | ||
|
|
c46a59e3b1 | ||
|
|
8a3a21972a | ||
|
|
6839669e04 | ||
|
|
5645e20505 | ||
|
|
15fe8f6261 | ||
|
|
855fafa94d | ||
|
|
4e10335ae7 | ||
|
|
21ea9ce579 | ||
|
|
ea07bbf4f9 | ||
|
|
8eed6fbe21 | ||
|
|
0859f2fbe9 | ||
|
|
71ba33edeb | ||
|
|
ce80d92b35 | ||
|
|
f17cf9b736 | ||
|
|
98579c774b | ||
|
|
509fa279e7 | ||
|
|
091ee87761 | ||
|
|
c0c20cc92e | ||
|
|
1341f7bca1 | ||
|
|
fe93db1d4b | ||
|
|
5db97ea773 | ||
|
|
1758e4f320 | ||
|
|
1a897c0419 | ||
|
|
32db3e3179 | ||
|
|
d830a8957a | ||
|
|
652ea48223 | ||
|
|
8c25b2b8f5 | ||
|
|
db4c56be8e | ||
|
|
f3bcffe2f9 | ||
|
|
bc5786d099 | ||
|
|
5959fe5daf | ||
|
|
649d338bb1 | ||
|
|
dcb058a718 | ||
|
|
1772bb5e7f | ||
|
|
30bd0adb06 | ||
|
|
44dfcf771c | ||
|
|
a1f8a7a930 | ||
|
|
b22887dfad | ||
|
|
28137fa3f4 | ||
|
|
9cbdc6055d | ||
|
|
fc93c61fcf | ||
|
|
3fa2e7ebd1 | ||
|
|
818628da0c | ||
|
|
adf90a8263 | ||
|
|
362923ac64 | ||
|
|
7d73838ce1 | ||
|
|
b3278ca9ec | ||
|
|
5707914ad5 | ||
|
|
9b697b24d8 |
28
db_update.py
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
42
eos/db/migrations/upgrade38.py
Normal 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))
|
||||
30
eos/db/migrations/upgrade39.py
Normal 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))
|
||||
@@ -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
|
||||
|
||||
|
||||
516
eos/effects.py
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 452 B |
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 725 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 761 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 915 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 898 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 834 B After Width: | Height: | Size: 894 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 856 B After Width: | Height: | Size: 938 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 858 B After Width: | Height: | Size: 944 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 837 B After Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 962 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 696 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 931 B After Width: | Height: | Size: 955 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 835 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 851 B After Width: | Height: | Size: 947 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 797 B After Width: | Height: | Size: 894 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 1022 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1002 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 911 B After Width: | Height: | Size: 937 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 743 B After Width: | Height: | Size: 814 B |