Merge branch 'master' into sso_v2

This commit is contained in:
Anton Vorobyov
2021-10-26 17:03:24 +03:00
committed by GitHub
227 changed files with 60733 additions and 18571 deletions

View File

@@ -27,6 +27,8 @@ import re
import sqlite3
import sys
from sqlalchemy import or_
# todo: need to set the EOS language to en, becasuse this assumes it's being run within an English context
# Need to know what that would do if called from pyfa
@@ -591,7 +593,10 @@ def update_db():
# pyfa, we can do it here as a post-processing step
for attr in eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(eos.gamedata.Attribute.ID == 1367).all():
attr.value = 4.0
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.name.like('%abyssal%')).all():
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(or_(
eos.gamedata.Item.name.like('%abyssal%'),
eos.gamedata.Item.name.like('%mutated%')
)).all():
item.published = False
for x in [

View File

@@ -118,7 +118,7 @@ from eos.db.gamedata import alphaClones, attribute, category, effect, group, ite
pyfalog.debug('Importing saveddata DB scheme')
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
miscData, mutator, module, override, price, queries, skill, targetProfile, user
miscData, mutatorMod, mutatorDrone, module, override, price, queries, skill, targetProfile, user
pyfalog.debug('Importing gamedata queries')
# noinspection PyPep8

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection

View File

@@ -0,0 +1,18 @@
"""
Migration 45
- Drone mutaplasmid support
"""
import sqlalchemy
def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT baseItemID FROM drones LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN baseItemID INTEGER;")
try:
saveddata_engine.execute("SELECT mutaplasmidID FROM drones LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute("ALTER TABLE drones ADD COLUMN mutaplasmidID INTEGER;")

View File

@@ -1,7 +1,8 @@
__all__ = [
"character",
"fit",
"mutator",
"mutatorMod",
"mutatorDrone",
"module",
"user",
"skill",

View File

@@ -18,27 +18,35 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
from sqlalchemy.orm import mapper, relation
from sqlalchemy.orm import mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.drone import Drone
from eos.saveddata.fit import Fit
from eos.saveddata.mutator import MutatorDrone
drones_table = Table("drones", saveddata_meta,
Column("groupID", Integer, primary_key=True),
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
Column("itemID", Integer, nullable=False),
Column("baseItemID", Integer, nullable=True),
Column("mutaplasmidID", Integer, nullable=True),
Column("amount", Integer, nullable=False),
Column("amountActive", Integer, nullable=False),
Column("projected", Boolean, default=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
Column("projectionRange", Float, nullable=True)
)
Column("projectionRange", Float, nullable=True))
mapper(Drone, drones_table,
properties={
"owner": relation(Fit)
}
)
"ID": synonym("groupID"),
"owner": relation(Fit),
"mutators": relation(
MutatorDrone,
backref="item",
cascade="all,delete-orphan",
collection_class=attribute_mapped_collection('attrID'))})

View File

@@ -18,14 +18,14 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm import relation, mapper
from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.module import Module
from eos.saveddata.mutator import Mutator
from eos.saveddata.fit import Fit
from eos.saveddata.mutator import MutatorModule
modules_table = Table("modules", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -45,13 +45,12 @@ modules_table = Table("modules", saveddata_meta,
Column("projectionRange", Float, nullable=True),
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
mapper(Module, modules_table,
properties={
"owner": relation(Fit),
"mutators": relation(
Mutator,
backref="module",
MutatorModule,
backref="item",
cascade="all,delete-orphan",
collection_class=attribute_mapped_collection('attrID')
)
})
collection_class=attribute_mapped_collection('attrID'))})

View File

@@ -23,13 +23,14 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.saveddata.mutator import Mutator
from eos.saveddata.mutator import MutatorDrone
mutator_table = Table("mutators", saveddata_meta,
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mutatorDrones_table = Table(
"mutatorsDrones", saveddata_meta,
Column("groupID", Integer, ForeignKey("drones.groupID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mapper(Mutator, mutator_table)
mapper(MutatorDrone, mutatorDrones_table)

View File

@@ -0,0 +1,36 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import datetime
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.saveddata.mutator import MutatorModule
mutatorMods_table = Table(
"mutators", saveddata_meta,
Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
Column("attrID", Integer, primary_key=True, index=True),
Column("value", Float, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
mapper(MutatorModule, mutatorMods_table)

View File

@@ -61,8 +61,9 @@ class Effect10(BaseEffect):
Used by:
Celestials from group: Destructible Effect Beacon (6 of 6)
Drones from group: Combat Drone (75 of 75)
Drones from group: Combat Drone (79 of 79)
Modules from group: Energy Weapon (212 of 214)
Celestial: EDENCOM Stockpile Non-Interactable (Do not translate)
"""
dealsDamage = True
@@ -614,8 +615,7 @@ class Effect101(BaseEffect):
# Lockbreaker Bombs
ecmStrengthBonus = src.getModifiedChargeAttr('scan{0}StrengthBonus'.format(fit.scanType))
if ecmStrengthBonus:
strModifier = 1 - min(1, ecmStrengthBonus / fit.scanStrength)
fit.ecmProjectedStr *= strModifier
fit.addProjectedEcm(ecmStrengthBonus)
elif src.item.group.name == 'Interdiction Sphere Launcher':
speedFactor = src.getModifiedChargeAttr('speedFactor')
if speedFactor:
@@ -871,7 +871,7 @@ class Effect230(BaseEffect):
Used by:
Implants named like: Eifyr and Co. 'Rogue' Afterburner AB (6 of 6)
Implants named like: Eifyr and Co. 'Rogue' Agility (2 of 2)
Implant: Serenity Anniversary Limited 'Overclocker' Dose
Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3)
Implant: Zor's Custom Navigation Link
Skill: Afterburner
"""
@@ -907,7 +907,7 @@ class Effect242(BaseEffect):
Used by:
Implants named like: Eifyr and Co. 'Rogue' Acceleration Control AC (6 of 6)
Implants named like: Eifyr and Co. 'Rogue' Agility (2 of 2)
Implant: Serenity Anniversary Limited 'Overclocker' Dose
Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3)
"""
type = 'passive'
@@ -1109,9 +1109,9 @@ class Effect391(BaseEffect):
Implants named like: Inherent Implants 'Highwall' Mining MX (3 of 3)
Implants named like: Mining Blitz Yield Booster Dose (3 of 3)
Implants named like: ORE 'Harvester' Efficiency (2 of 2)
Implants named like: Serenity Limited 'Efficiency' Dose (3 of 3)
Implants named like: Serenity Poteque 'Prospector' Harvesting MC (3 of 3)
Implant: Michi's Excavation Augmentor
Implant: Serenity Anniversary Limited 'Efficiency' Dose
Skill: Astrogeology
Skill: Mining
"""
@@ -1133,8 +1133,8 @@ class Effect392(BaseEffect):
Implants named like: Capsuleer Defense Augmentation Chip (3 of 3)
Implants named like: Festival only 'Rock' HH Dose (4 of 4)
Implants named like: Inherent Implants 'Noble' Mechanic MC (6 of 6)
Implants named like: Serenity Limited 'Hardshell' Dose (3 of 3)
Modules named like: Transverse Bulkhead (8 of 8)
Implant: Serenity Anniversary Limited 'Hardshell' Dose
Skill: Mechanics
"""
@@ -1156,7 +1156,7 @@ class Effect394(BaseEffect):
Implants named like: grade Snake (16 of 18)
Modules named like: Auxiliary Thrusters (8 of 8)
Implant: Grand Prix Velocity Booster
Implant: Quafe Zero
Implant: Quafe Zero Classic
Implant: Serenity YC122.9 Season Booster - Max Velocity
Skill: Navigation
"""
@@ -1182,6 +1182,7 @@ class Effect395(BaseEffect):
Implants named like: grade Nomad (10 of 12)
Modules named like: Low Friction Nozzle Joints (8 of 8)
Implant: Genolution Core Augmentation CA-4
Implant: Quafe Zero Green Apple
Implant: Serenity YC122.9 Season Booster - Agility
Skill: Evasive Maneuvering
Skill: Spaceship Command
@@ -1192,7 +1193,7 @@ class Effect395(BaseEffect):
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
level = container.level if 'skill' in context else 1
penalties = 'skill' not in context and 'implant' not in context
penalties = 'skill' not in context and 'implant' not in context and 'booster' not in context
fit.ship.boostItemAttr('agility', container.getModifiedItemAttr('agilityBonus') * level,
stackingPenalties=penalties, **kwargs)
@@ -1283,11 +1284,11 @@ class Effect446(BaseEffect):
Used by:
Implants named like: Capsuleer Defense Augmentation Chip (3 of 3)
Implants named like: Festival only 'Rock' SH Dose (4 of 4)
Implants named like: Serenity Limited 'Hardshell' Dose (3 of 3)
Implants named like: Zainou 'Gnome' Shield Management SM (6 of 6)
Modules named like: Core Defense Field Extender (8 of 8)
Implant: Genolution Core Augmentation CA-3
Implant: Sansha Modified 'Gnome' Implant
Implant: Serenity Anniversary Limited 'Hardshell' Dose
Skill: Shield Management
"""
@@ -1309,6 +1310,7 @@ class Effect485(BaseEffect):
Modules named like: Capacitor Control Circuit (8 of 8)
Implant: Basic Capsuleer Engineering Augmentation Chip
Implant: Genolution Core Augmentation CA-2
Implant: Quafe Zero Green Apple
Skill: Capacitor Systems Operation
"""
@@ -1738,9 +1740,8 @@ class Effect584(BaseEffect):
surgicalStrikeDamageMultiplierBonusPostPercentDamageMultiplierLocationShipModulesRequiringGunnery
Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implants named like: 'Pyrolancea' Dose (7 of 7)
Implants named like: Eifyr and Co. 'Gunslinger' Surgical Strike SS (6 of 6)
Implant: Serenity Anniversary Limited 'Pyrolancea' Dose
Implant: Standard Cerebral Accelerator
"""
@@ -2120,7 +2121,7 @@ class Effect699(BaseEffect):
Used by:
Implants named like: Zainou 'Gypsy' Signature Analysis SA (6 of 6)
Modules named like: Targeting System Subcontroller (8 of 8)
Implant: Quafe Zero
Implant: Quafe Zero Classic
Skill: Signature Analysis
"""
@@ -2463,10 +2464,10 @@ class Effect856(BaseEffect):
Used by:
Implants named like: Eifyr and Co. 'Rogue' Agility (2 of 2)
Implants named like: Eifyr and Co. 'Rogue' Warp Drive Speed WS (6 of 6)
Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3)
Implants named like: grade Ascendancy (10 of 12)
Modules named like: Hyperspatial Velocity Optimizer (8 of 8)
Implant: Grand Prix Warp Booster
Implant: Serenity Anniversary Limited 'Overclocker' Dose
Implant: Serenity YC122.9 Season Booster - Warp Speed
"""
@@ -3749,8 +3750,8 @@ class Effect1190(BaseEffect):
Used by:
Implants named like: Inherent Implants 'Yeti' Ice Harvesting IH (3 of 3)
Implants named like: ORE 'Harvester' Efficiency (2 of 2)
Implants named like: Serenity Limited 'Efficiency' Dose (3 of 3)
Implants named like: Serenity Poteque 'Prospector' Harvesting MC (3 of 3)
Implant: Serenity Anniversary Limited 'Efficiency' Dose
Module: Medium Ice Harvester Accelerator I
Skill: Ice Harvesting
"""
@@ -4259,9 +4260,9 @@ class Effect1409(BaseEffect):
systemScanDurationSkillAstrometrics
Used by:
Implants named like: Poteque 'Prospector' Astrometric Acquisition AQ (3 of 3)
Implants named like: Acquisition (6 of 6)
Implants named like: Poteque 'Prospector' Sharpeye (2 of 2)
Implant: Serenity Anniversary Limited 'Sharpeye' Dose
Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3)
Skill: Astrometric Acquisition
Skill: Astrometrics
"""
@@ -4404,7 +4405,6 @@ class Effect1446(BaseEffect):
ewSkillTpMaxRangeBonus
Used by:
Implants named like: Liberation Games EWar Booster (3 of 3)
Modules named like: Particle Dispersion Projector (8 of 8)
Skill: Long Distance Jamming
"""
@@ -4568,7 +4568,6 @@ class Effect1550(BaseEffect):
ewSkillTargetPaintingStrengthBonus
Used by:
Implants named like: Liberation Games EWar Booster (3 of 3)
Skill: Signature Focusing
"""
@@ -4722,7 +4721,6 @@ class Effect1590(BaseEffect):
missileSkillAoeVelocityBonus
Used by:
Implants named like: Liberation Games Accuracy Booster (3 of 3)
Implants named like: Zainou 'Deadeye' Target Navigation Prediction TN (6 of 6)
Modules named like: Warhead Flare Catalyst (8 of 8)
Skill: Target Navigation Prediction
@@ -4798,8 +4796,7 @@ class Effect1595(BaseEffect):
missileSkillWarheadUpgradesEmDamageBonus
Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implant: Serenity Anniversary Limited 'Pyrolancea' Dose
Implants named like: 'Pyrolancea' Dose (7 of 7)
Skill: Warhead Upgrades
"""
@@ -4817,8 +4814,7 @@ class Effect1596(BaseEffect):
missileSkillWarheadUpgradesExplosiveDamageBonus
Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implant: Serenity Anniversary Limited 'Pyrolancea' Dose
Implants named like: 'Pyrolancea' Dose (7 of 7)
Skill: Warhead Upgrades
"""
@@ -4836,8 +4832,7 @@ class Effect1597(BaseEffect):
missileSkillWarheadUpgradesKineticDamageBonus
Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implant: Serenity Anniversary Limited 'Pyrolancea' Dose
Implants named like: 'Pyrolancea' Dose (7 of 7)
Skill: Warhead Upgrades
"""
@@ -4855,7 +4850,7 @@ class Effect1615(BaseEffect):
shipAdvancedSpaceshipCommandAgilityBonus
Used by:
Items from market group: Ships > Capital Ships (40 of 41)
Items from market group: Ships > Capital Ships (41 of 41)
"""
type = 'passive'
@@ -4888,7 +4883,12 @@ class Effect1617(BaseEffect):
shipCapitalAgilityBonus
Used by:
Items from market group: Ships > Capital Ships (31 of 41)
Ships from group: Carrier (4 of 4)
Ships from group: Dreadnought (8 of 8)
Ships from group: Force Auxiliary (6 of 6)
Ships from group: Supercarrier (6 of 6)
Ships from group: Titan (7 of 7)
Ship: Rorqual
"""
type = 'passive'
@@ -5079,8 +5079,7 @@ class Effect1657(BaseEffect):
missileSkillWarheadUpgradesThermalDamageBonus
Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implant: Serenity Anniversary Limited 'Pyrolancea' Dose
Implants named like: 'Pyrolancea' Dose (7 of 7)
Skill: Warhead Upgrades
"""
@@ -5253,7 +5252,7 @@ class Effect1730(BaseEffect):
droneDmgBonus
Used by:
Skills from group: Drones (8 of 27)
Skills from group: Drones (9 of 28)
"""
type = 'passive'
@@ -5301,7 +5300,6 @@ class Effect1764(BaseEffect):
missileSkillMissileProjectileVelocityBonus
Used by:
Implants named like: Liberation Games Range Booster (3 of 3)
Implants named like: Zainou 'Deadeye' Missile Projection MP (6 of 6)
Modules named like: Hydraulic Bay Thrusters (8 of 8)
Skill: Missile Projection
@@ -6832,6 +6830,30 @@ class Effect2255(BaseEffect):
type = 'active'
class Effect2296(BaseEffect):
"""
modifyArmorResonancePassivePostPercentPassive
Used by:
Implants named like: Tetrimon Resistance Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
for srcResType, tgtResType in (
('Em', 'Em'),
('Explosive', 'Explosive'),
('Kinetic', 'Kinetic'),
('Thermic', 'Thermal')
):
fit.ship.boostItemAttr(
'armor{}DamageResonance'.format(tgtResType),
src.getModifiedItemAttr('passive{}DamageResistanceBonus'.format(srcResType)),
**kwargs)
class Effect2298(BaseEffect):
"""
scanStrengthBonusPercentPassive
@@ -6988,6 +7010,7 @@ class Effect2432(BaseEffect):
Used by:
Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6)
Implants named like: Mindflood Booster (4 of 4)
Implants named like: Tetrimon Capacitor Booster (3 of 3)
Modules named like: Semiconductor Memory Cell (8 of 8)
Implant: Antipharmakon Aeolis
Implant: Basic Capsuleer Engineering Augmentation Chip
@@ -7098,10 +7121,10 @@ class Effect2485(BaseEffect):
Implants named like: Capsuleer Defense Augmentation Chip (3 of 3)
Implants named like: Festival only 'Rock' AH Dose (4 of 4)
Implants named like: Inherent Implants 'Noble' Hull Upgrades HG (7 of 7)
Implants named like: Serenity Limited 'Hardshell' Dose (3 of 3)
Implant: Genolution Core Augmentation CA-4
Implant: Imperial Navy Modified 'Noble' Implant
Implant: Imperial Special Ops Field Enhancer - Standard
Implant: Serenity Anniversary Limited 'Hardshell' Dose
"""
type = 'passive'
@@ -7589,7 +7612,6 @@ class Effect2695(BaseEffect):
falloffBonusEffectProjectiles
Used by:
Implants named like: Liberation Games Range Booster (3 of 3)
Modules named like: Projectile Ambit Extension (8 of 8)
"""
@@ -7608,6 +7630,7 @@ class Effect2696(BaseEffect):
maxRangeBonusEffectLasers
Used by:
Implants named like: Tetrimon Precision Booster (3 of 3)
Modules named like: Energy Locus Coordinator (8 of 8)
"""
@@ -7615,9 +7638,10 @@ class Effect2696(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
penalties = 'booster' not in context
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Energy Weapon',
'maxRange', module.getModifiedItemAttr('maxRangeBonus'),
stackingPenalties=True, **kwargs)
stackingPenalties=penalties, **kwargs)
class Effect2697(BaseEffect):
@@ -7642,7 +7666,6 @@ class Effect2698(BaseEffect):
maxRangeBonusEffectProjectiles
Used by:
Implants named like: Liberation Games Range Booster (3 of 3)
Modules named like: Projectile Locus Coordinator (8 of 8)
"""
@@ -8287,7 +8310,6 @@ class Effect2798(BaseEffect):
projectileWeaponDamageMultiplyPassive
Used by:
Implants named like: Liberation Games Damage Booster (3 of 3)
Modules named like: Projectile Collision Accelerator (8 of 8)
"""
@@ -8357,6 +8379,7 @@ class Effect2803(BaseEffect):
energyWeaponDamageMultiplyPassive
Used by:
Implants named like: Harvest Damage Booster (3 of 3)
Modules named like: Energy Collision Accelerator (8 of 8)
"""
@@ -8364,9 +8387,10 @@ class Effect2803(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
penalties = 'booster' not in context
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == 'Energy Weapon',
'damageMultiplier', module.getModifiedItemAttr('damageMultiplier'),
stackingPenalties=True, **kwargs)
stackingPenalties=penalties, **kwargs)
class Effect2804(BaseEffect):
@@ -8554,7 +8578,6 @@ class Effect2851(BaseEffect):
missileDMGBonusPassive
Used by:
Implants named like: Liberation Games Damage Booster (3 of 3)
Modules named like: Warhead Calefaction Catalyst (8 of 8)
"""
@@ -8783,8 +8806,8 @@ class Effect2885(BaseEffect):
Used by:
Implants named like: Eifyr and Co. 'Alchemist' Gas Harvesting GH (3 of 3)
Implants named like: ORE 'Harvester' Efficiency (2 of 2)
Implants named like: Serenity Limited 'Efficiency' Dose (3 of 3)
Implants named like: Serenity Poteque 'Prospector' Harvesting MC (3 of 3)
Implant: Serenity Anniversary Limited 'Efficiency' Dose
"""
type = 'passive'
@@ -9638,11 +9661,10 @@ class Effect3182(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
if 'projected' not in context:
for scanType in ('Gravimetric', 'Magnetometric', 'Radar', 'Ladar'):
module.boostItemAttr('scan{0}StrengthBonus'.format(scanType),
module.getModifiedItemAttr('overloadECMStrengthBonus'),
stackingPenalties=True, **kwargs)
for scanType in ('Gravimetric', 'Magnetometric', 'Radar', 'Ladar'):
module.boostItemAttr('scan{0}StrengthBonus'.format(scanType),
module.getModifiedItemAttr('overloadECMStrengthBonus'),
stackingPenalties=True, **kwargs)
class Effect3196(BaseEffect):
@@ -13529,9 +13551,9 @@ class Effect4161(BaseEffect):
baseMaxScanDeviationModifierRequiringAstrometrics
Used by:
Implants named like: Poteque 'Prospector' Astrometric Pinpointing AP (3 of 3)
Implants named like: Pinpointing (6 of 6)
Implants named like: Poteque 'Prospector' Sharpeye (2 of 2)
Implant: Serenity Anniversary Limited 'Sharpeye' Dose
Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3)
Skill: Astrometric Pinpointing
Skill: Astrometrics
"""
@@ -13552,11 +13574,11 @@ class Effect4162(BaseEffect):
Used by:
Modules from group: Scan Probe Launcher (4 of 7)
Implants named like: Poteque 'Prospector' Astrometric Rangefinding AR (3 of 3)
Implants named like: Poteque 'Prospector' Sharpeye (2 of 2)
Implants named like: Rangefinding (6 of 6)
Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3)
Implants named like: grade Virtue (10 of 12)
Modules named like: Gravity Capacitor Upgrade (8 of 8)
Implant: Serenity Anniversary Limited 'Sharpeye' Dose
Implant: Serenity YC122.9 Season Booster - Probe Scanning Strength
Skill: Astrometric Rangefinding
Skill: Astrometrics
@@ -13567,7 +13589,7 @@ class Effect4162(BaseEffect):
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
level = container.level if 'skill' in context else 1
penalized = False if 'skill' in context or 'implant' in context else True
penalized = False if 'skill' in context or 'implant' in context or 'booster' in context else True
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill('Astrometrics'),
'baseSensorStrength', container.getModifiedItemAttr('scanStrengthBonus') * level,
stackingPenalties=penalized, **kwargs)
@@ -18148,6 +18170,7 @@ class Effect5189(BaseEffect):
trackingSpeedBonusEffectLasers
Used by:
Implants named like: Tetrimon Precision Booster (3 of 3)
Modules named like: Energy Metastasis Adjuster (8 of 8)
"""
@@ -18155,9 +18178,10 @@ class Effect5189(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
penalties = 'booster' not in context
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Energy Weapon',
'trackingSpeed', module.getModifiedItemAttr('trackingSpeedBonus'),
stackingPenalties=True, **kwargs)
stackingPenalties=penalties, **kwargs)
class Effect5190(BaseEffect):
@@ -18165,7 +18189,6 @@ class Effect5190(BaseEffect):
trackingSpeedBonusEffectProjectiles
Used by:
Implants named like: Liberation Games Accuracy Booster (3 of 3)
Modules named like: Projectile Metastasis Adjuster (8 of 8)
"""
@@ -24319,7 +24342,12 @@ class Effect6104(BaseEffect):
entosisDurationMultiply
Used by:
Items from market group: Ships > Capital Ships (31 of 41)
Ships from group: Carrier (4 of 4)
Ships from group: Dreadnought (8 of 8)
Ships from group: Force Auxiliary (6 of 6)
Ships from group: Supercarrier (6 of 6)
Ships from group: Titan (7 of 7)
Ship: Rorqual
"""
type = 'passive'
@@ -27265,8 +27293,7 @@ class Effect6437(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6439(BaseEffect):
@@ -27460,8 +27487,7 @@ class Effect6470(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6472(BaseEffect):
@@ -27769,14 +27795,11 @@ class Effect6502(BaseEffect):
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
fit.ship.boostItemAttr('armorExplosiveDamageResonance', src.getModifiedItemAttr('shipBonusDreadnoughtA2'),
skill='Amarr Dreadnought', **kwargs)
fit.ship.boostItemAttr('armorEmDamageResonance', src.getModifiedItemAttr('shipBonusDreadnoughtA2'),
skill='Amarr Dreadnought', **kwargs)
fit.ship.boostItemAttr('armorThermalDamageResonance', src.getModifiedItemAttr('shipBonusDreadnoughtA2'),
skill='Amarr Dreadnought', **kwargs)
fit.ship.boostItemAttr('armorKineticDamageResonance', src.getModifiedItemAttr('shipBonusDreadnoughtA2'),
skill='Amarr Dreadnought', **kwargs)
for dmgType in ('em', 'thermal', 'kinetic', 'explosive'):
fit.ship.boostItemAttr(
'armor{}DamageResonance'.format(dmgType.capitalize()),
src.getModifiedItemAttr('shipBonusDreadnoughtA2'),
skill='Amarr Dreadnought', **kwargs)
class Effect6503(BaseEffect):
@@ -27954,8 +27977,7 @@ class Effect6513(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6526(BaseEffect):
@@ -28275,7 +28297,7 @@ class Effect6556(BaseEffect):
moduleBonusDroneDamageAmplifier
Used by:
Modules from group: Drone Damage Modules (11 of 11)
Modules from group: Drone Damage Modules (12 of 12)
Modules named like: C3 'Hivaa Saitsuo' Ballistic Control System (2 of 2)
Module: Abyssal Ballistic Control System
"""
@@ -28532,7 +28554,7 @@ class Effect6566(BaseEffect):
moduleBonusFighterSupportUnit
Used by:
Modules from group: Fighter Support Unit (8 of 8)
Modules from group: Fighter Support Unit (9 of 9)
"""
type = 'passive'
@@ -30181,9 +30203,6 @@ class Effect6658(BaseEffect):
# Speed penalty
fit.ship.boostItemAttr('maxVelocity', src.getModifiedItemAttr('speedFactor'), **kwargs)
# Max locked targets
fit.ship.forceItemAttr('maxLockedTargets', src.getModifiedItemAttr('maxLockedTargets'), **kwargs)
# new with April 2016 release
for scanType in ('Magnetometric', 'Ladar', 'Gravimetric', 'Radar'):
fit.ship.boostItemAttr('scan{}Strength'.format(scanType),
@@ -30586,8 +30605,7 @@ class Effect6685(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6686(BaseEffect):
@@ -30846,8 +30864,7 @@ class Effect6695(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6697(BaseEffect):
@@ -31133,6 +31150,7 @@ class Effect6714(BaseEffect):
"""
type = 'projected', 'active'
runTime = 'late'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
@@ -31146,8 +31164,7 @@ class Effect6714(BaseEffect):
if 'effect' in kwargs:
from eos.modifiedAttributeDict import ModifiedAttributeDict
strength *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
chanceModifier = 1 - min(1, strength / fit.scanStrength)
fit.ecmProjectedStr *= chanceModifier
fit.addProjectedEcm(strength)
class Effect6717(BaseEffect):
@@ -34065,7 +34082,6 @@ class Effect7020(BaseEffect):
Used by:
Implants named like: Inquest 'Eros' Stasis Webifier MR (3 of 3)
Implants named like: Inquest 'Hedone' Entanglement Optimizer WS (3 of 3)
Implants named like: Liberation Games EWar Booster (3 of 3)
"""
type = 'passive'
@@ -35648,7 +35664,7 @@ class Effect7193(BaseEffect):
systemMiningCycleTimeBonus
Used by:
Celestials named like: Stellar Observatory (4 of 4)
Celestials from group: Destructible Effect Beacon (4 of 6)
Celestials named like: Triglavian Invasion System Effects (3 of 3)
"""
@@ -37553,3 +37569,71 @@ class Effect8158(BaseEffect):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Cloaking'), 'stabilizeCloakDuration',
booster.getModifiedItemAttr('stabilizeCloakDurationBonus'), **kwargs)
class Effect8267(BaseEffect):
"""
weaponDisruptorResistanceBonusPassive
Used by:
Implants named like: Harvest Anti Disruptor Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.ship.boostItemAttr(
'weaponDisruptionResistance',
container.getModifiedItemAttr('weaponDisruptionResistanceBonus'), **kwargs)
class Effect8268(BaseEffect):
"""
nosferatuDurationBonusPassive
Used by:
Implants named like: Harvest Nosferatu Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.group.name == 'Energy Nosferatu', 'duration',
module.getModifiedItemAttr('durationBonus'), **kwargs)
class Effect8269(BaseEffect):
"""
stasisWebifierMaxRangeAddPassive
Used by:
Implants named like: Harvest Webifier Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemIncrease(
lambda mod: mod.item.group.name == 'Stasis Web', 'maxRange',
module.getModifiedItemAttr('stasisWebRangeAdd'), **kwargs)
class Effect8270(BaseEffect):
"""
capacitorWarfareResistanceBonusPassive
Used by:
Implants named like: Tetrimon Anti Drain Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.ship.boostItemAttr(
'energyWarfareResistance',
container.getModifiedItemAttr('energyWarfareResistanceBonus'), **kwargs)

View File

@@ -567,7 +567,18 @@ class Group(EqBase):
class DynamicItem(EqBase):
pass
@property
def shortName(self):
name = self.item.customName
keywords = ('Decayed', 'Gravid', 'Unstable', 'Radical')
for kw in keywords:
if name.startswith(f'{kw} '):
name = kw
m = re.match('(?P<mutagrade>\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\S+) Mutaplasmid', name)
if m:
name = '{} {}'.format(m.group('mutagrade'), m.group('mutatype'))
return name
class DynamicItemAttribute(EqBase):

View File

@@ -25,6 +25,8 @@ from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
from eos.saveddata.mutator import MutatorDrone
from eos.utils.cycles import CycleInfo
from eos.utils.default import DEFAULT
from eos.utils.stats import DmgTypes, RRTypes
@@ -33,12 +35,13 @@ from eos.utils.stats import DmgTypes, RRTypes
pyfalog = Logger(__name__)
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
MINING_ATTRIBUTES = ("miningAmount",)
def __init__(self, item):
def __init__(self, item, baseItem=None, mutaplasmid=None):
"""Initialize a drone from the program"""
self.__item = item
self._item = item
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if self.isInvalid:
raise ValueError("Passed item is not a Drone")
@@ -53,14 +56,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a drone from the database and validate"""
self.__item = None
self._item = None
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
self._item = eos.db.getItem(self.itemID)
if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
try:
self._mutaReconstruct()
except MutaError:
return
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
return
@@ -74,10 +82,13 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__baseRRAmount = None
self.__miningyield = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__itemModifiedAttributes.original = self.__item.attributes
self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__itemModifiedAttributes.original = self._item.attributes
self.__itemModifiedAttributes.overrides = self._item.overrides
self.__chargeModifiedAttributes = ModifiedAttributeDict()
self._mutaLoadMutators(mutatorClass=MutatorDrone)
self.__itemModifiedAttributes.mutators = self.mutators
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
if chargeID is not None:
@@ -96,11 +107,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def isInvalid(self):
return self.__item is None or self.__item.category.name != "Drone"
if self._item is None:
return True
if self._item.category.name != "Drone":
return True
if self._mutaIsInvalid:
return True
return False
@property
def item(self):
return self.__item
return self._item
@property
def charge(self):
@@ -337,10 +354,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
def __deepcopy__(self, memo):
copy = Drone(self.item)
copy = Drone(self.item, self.baseItem, self.mutaplasmid)
copy.amount = self.amount
copy.amountActive = self.amountActive
copy.projectionRange = self.projectionRange
self._mutaApplyMutators(mutatorClass=MutatorDrone, targetInstance=copy)
return copy
def rebase(self, item):
@@ -348,10 +366,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
amountActive = self.amountActive
projectionRange = self.projectionRange
Drone.__init__(self, item)
Drone.__init__(self, item, self.baseItem, self.mutaplasmid)
self.amount = amount
self.amountActive = amountActive
self.projectionRange = projectionRange
self._mutaApplyMutators(mutatorClass=MutatorDrone)
def fits(self, fit):
fitDroneGroupLimits = set()

View File

@@ -155,7 +155,7 @@ class Fit:
self.factorReload = False
self.boostsFits = set()
self.gangBoosts = None
self.ecmProjectedStr = 1
self.__ecmProjectedList = []
self.commandBonuses = {}
def clearFactorReloadDependentData(self):
@@ -411,7 +411,11 @@ class Fit:
@property
def jamChance(self):
return (1 - self.ecmProjectedStr) * 100
sensors = self.scanStrength
retainLockChance = 1
for jamStr in self.__ecmProjectedList:
retainLockChance *= 1 - min(1, jamStr / sensors)
return (1 - retainLockChance) * 100
@property
def maxSpeed(self):
@@ -499,7 +503,7 @@ class Fit:
self.__capUsed = None
self.__capRecharge = None
self.__savedCapSimData.clear()
self.ecmProjectedStr = 1
self.__ecmProjectedList = []
# self.commandBonuses = {}
del self.__calculatedTargets[:]
@@ -562,6 +566,9 @@ class Fit:
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value):
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
def addProjectedEcm(self, strength):
self.__ecmProjectedList.append(strength)
def __runCommandBoosts(self, runTime="normal"):
pyfalog.debug("Applying gang boosts for {0}", repr(self))
for warfareBuffID in list(self.commandBonuses.keys()):

View File

@@ -27,7 +27,8 @@ from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel
from eos.saveddata.mutator import Mutator
from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
from eos.saveddata.mutator import MutatorModule
from eos.utils.cycles import CycleInfo, CycleSequence
from eos.utils.default import DEFAULT
from eos.utils.float import floatUnerr
@@ -61,7 +62,7 @@ ProjectedSystem = {
}
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
"""An instance of this class represents a module together with its charge and modified attributes"""
MINING_ATTRIBUTES = ("miningAmount",)
SYSTEM_GROUPS = (
@@ -72,21 +73,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
"""Initialize a module from the program"""
self.itemID = item.ID if item is not None else None
self.baseItemID = baseItem.ID if baseItem is not None else None
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
if baseItem is not None:
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
else:
self.__item = item
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
self._item = item
self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if item is not None and self.isInvalid:
raise ValueError("Passed item is not a Module")
@@ -101,27 +90,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a module from the database and validate"""
self.__item = None
self.__baseItem = None
self._item = None
self.__charge = None
self.__mutaplasmid = None
# we need this early if module is invalid and returns early
self.__slot = self.dummySlot
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
self._item = eos.db.getItem(self.itemID)
if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.baseItemID:
self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = eos.db.getItem(self.baseItemID)
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
if self.__baseItem is None:
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
return
try:
self._mutaReconstruct()
except MutaError:
return
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
@@ -149,21 +133,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
self.__slot = self.dummySlot # defaults to None
if self.__item:
self.__itemModifiedAttributes.original = self.__item.attributes
self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__hardpoint = self.__calculateHardpoint(self.__item)
self.__slot = self.calculateSlot(self.__item)
# Instantiate / remove mutators if this is a mutated module
if self.__baseItem:
for x in self.mutaplasmid.attributes:
attr = self.item.attributes[x.name]
id = attr.ID
if id not in self.mutators: # create the mutator
Mutator(self, attr, attr.value)
# @todo: remove attributes that are no longer part of the mutaplasmid.
if self._item:
self.__itemModifiedAttributes.original = self._item.attributes
self.__itemModifiedAttributes.overrides = self._item.overrides
self.__hardpoint = self.__calculateHardpoint(self._item)
self.__slot = self.calculateSlot(self._item)
self._mutaLoadMutators(mutatorClass=MutatorModule)
self.__itemModifiedAttributes.mutators = self.mutators
if self.__charge:
@@ -198,28 +174,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# todo: validate baseItem as well if it's set.
if self.isEmpty:
return False
if self.__item is None:
if self._item is None:
return True
if (
self.__item.category.name not in ("Module", "Subsystem", "Structure Module")
and self.__item.group.name not in self.SYSTEM_GROUPS
self._item.category.name not in ("Module", "Subsystem", "Structure Module")
and self._item.group.name not in self.SYSTEM_GROUPS
):
return True
if (
self.__item.category.name == "Structure Module"
and self.__item.group.name == "Quantum Cores"
self._item.category.name == "Structure Module"
and self._item.group.name == "Quantum Cores"
):
return True
if self.item.isAbyssal and not self.isMutated:
return True
if self.isMutated and not self.__mutaplasmid:
if self._mutaIsInvalid:
return True
return False
@property
def isMutated(self):
return self.baseItemID and self.mutaplasmidID
@property
def numCharges(self):
return self.getNumCharges(self.charge)
@@ -419,15 +389,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def item(self):
return self.__item if self.__item != 0 else None
@property
def baseItem(self):
return self.__baseItem
@property
def mutaplasmid(self):
return self.__mutaplasmid
return self._item if self._item != 0 else None
@property
def charge(self):
@@ -1098,9 +1060,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
copy.spoolType = self.spoolType
copy.spoolAmount = self.spoolAmount
copy.projectionRange = self.projectionRange
for x in self.mutators.values():
Mutator(copy, x.attribute, x.value)
self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
return copy
@@ -1118,14 +1078,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.spoolType = spoolType
self.spoolAmount = spoolAmount
self.projectionRange = projectionRange
for x in self.mutators.values():
Mutator(self, x.attribute, x.value)
self._mutaApplyMutators(mutatorClass=MutatorModule)
def __repr__(self):
if self.item:
return "Module(ID={}, name={}) at {}".format(
self.item.ID, self.item.name, hex(id(self))
)
return "Module(ID={}, name={}) at {}".format(self.item.ID, self.item.name, hex(id(self)))
else:
return "EmptyModule() at {}".format(hex(id(self)))

View File

@@ -0,0 +1,105 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import eos.db
from logbook import Logger
pyfalog = Logger(__name__)
class MutaError(Exception):
pass
class MutatedMixin:
@property
def isMutated(self):
return bool(self.baseItemID and self.mutaplasmidID)
@property
def baseItem(self):
return self.__baseItem
@property
def mutaplasmid(self):
return self.__mutaplasmid
@property
def fullName(self):
if self.isMutated:
mutaShortName = self.mutaplasmid.shortName
mutaFullName = self.mutaplasmid.item.customName
# Short name can be unavailable for non-english language
if mutaShortName != mutaFullName:
return f'{self.mutaplasmid.shortName} {self.baseItem.customName}'
return self.item.customName
def _mutaInit(self, baseItem, mutaplasmid):
self.baseItemID = baseItem.ID if baseItem is not None else None
self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
if baseItem is not None:
# we're working with a mutated module, need to get abyssal module loaded with the base attributes
# Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
# will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
# before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
self._item = eos.db.getItemWithBaseItemAttribute(self._item.ID, self.baseItemID)
self.__baseItem = baseItem
self.__mutaplasmid = mutaplasmid
else:
self.__baseItem = None
self.__mutaplasmid = None
def _mutaReconstruct(self):
self.__baseItem = None
self.__mutaplasmid = None
if self.baseItemID:
self._item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
self.__baseItem = eos.db.getItem(self.baseItemID)
self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
if self.__baseItem is None:
pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
raise MutaError
def _mutaLoadMutators(self, mutatorClass):
# Instantiate / remove mutators if this is a mutated module
if self.__baseItem:
for x in self.mutaplasmid.attributes:
attr = self.item.attributes[x.name]
id = attr.ID
if id not in self.mutators: # create the mutator
mutatorClass(self, attr, attr.value)
# @todo: remove attributes that are no longer part of the mutaplasmid.
@property
def _mutaIsInvalid(self):
if self.item.isAbyssal and not self.isMutated:
return True
if self.isMutated and not self.__mutaplasmid:
return True
return False
def _mutaApplyMutators(self, mutatorClass, targetInstance=None):
if targetInstance is None:
targetInstance = self
for x in self.mutators.values():
mutatorClass(targetInstance, x.attribute, x.value)

View File

@@ -27,10 +27,10 @@ from eos.eqBase import EqBase
pyfalog = Logger(__name__)
class Mutator(EqBase):
""" Mutators are the object that represent an attribute override on the module level, in conjunction with
mutaplasmids. Each mutated module, when created, is instantiated with a list of these objects, dictated by the
mutaplasmid that is used on the base module.
class MutatorBase(EqBase):
""" Mutators are the object that represent an attribute override on the eos item level, in conjunction with
mutaplasmids. Each mutated item, when created, is instantiated with a list of these objects, dictated by the
mutaplasmid that is used on the base item.
A note on the different attributes on this object:
* attribute: points to the definition of the attribute from dgmattribs.
@@ -40,13 +40,13 @@ class Mutator(EqBase):
This could probably be cleaned up with smarter relationships, but whatever
"""
def __init__(self, module, attr, value):
# this needs to be above module assignment, as assigning the module will add it to the list and it via
def __init__(self, item, attr, value):
# this needs to be above item assignment, as assigning the item will add it to the list and it via
# relationship and needs this set 4correctly
self.attrID = attr.ID
self.module = module
self.moduleID = module.ID
self.item = item
self.itemID = item.ID
self.__attr = attr
self.build()
@@ -67,20 +67,20 @@ class Mutator(EqBase):
def build(self):
# try...except here to catch orphaned mutators. Pretty rare, only happens so far if hacking the database
# But put it here to remove the module link if it happens, until a better solution can be developed
# But put it here to remove the eos item link if it happens, until a better solution can be developed
try:
# dynamic attribute links to the Mutaplasmids attribute definition for this mutated definition
self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID)
self.dynamicAttribute = next(a for a in self.item.mutaplasmid.attributes if a.attributeID == self.attrID)
# base attribute links to the base ite's attribute for this mutated definition (contains original, base value)
self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name]
self.baseAttribute = self.item.item.attributes[self.dynamicAttribute.name]
except (KeyboardInterrupt, SystemExit):
raise
except:
self.module = None
self.item = None
@validates("value")
def validator(self, key, val):
""" Validates values as properly falling within the range of the modules' Mutaplasmid """
""" Validates values as properly falling within the range of the items' Mutaplasmid """
if self.baseValue == 0:
return 0
mod = val / self.baseValue
@@ -99,7 +99,7 @@ class Mutator(EqBase):
# @todo: need to test what happens:
# 1) if an attribute is removed from the EVE database
# 2) if a mutaplasmid does not have the attribute anymore
# 3) if a mutaplasmid does not exist (in eve or on the module's item)
# 3) if a mutaplasmid does not exist (in eve or on the pyfa item's item)
# Can remove invalid ones in a SQLAlchemy collection class... eventually
return self.__attr is None
@@ -139,3 +139,11 @@ class Mutator(EqBase):
@property
def attribute(self):
return self.__attr
class MutatorModule(MutatorBase):
pass
class MutatorDrone(MutatorBase):
pass

View File

@@ -193,11 +193,8 @@ class DroneView(Display):
@staticmethod
def droneKey(drone):
sMkt = Market.getInstance()
groupName = sMkt.getMarketGroupByItem(drone.item).marketGroupName
return (DRONE_ORDER.index(groupName), drone.item.name)
groupName = Market.getInstance().getMarketGroupByItem(drone.item).marketGroupName
return (DRONE_ORDER.index(groupName), drone.isMutated, drone.fullName)
def fitChanged(self, event):
event.Skip()

View File

@@ -26,7 +26,7 @@ from gui.builtinContextMenus import itemAmountChange
from gui.builtinContextMenus import itemProjectionRange
from gui.builtinContextMenus import droneSplitStack
from gui.builtinContextMenus import itemVariationChange
from gui.builtinContextMenus import moduleMutations
from gui.builtinContextMenus import itemMutations
from gui.builtinContextMenus import moduleFill
from gui.builtinContextMenus import moduleMutatedExport
from gui.builtinContextMenus import skillAffectors

View File

@@ -5,7 +5,7 @@ from gui import fitCommands as cmd
from gui.contextMenu import ContextMenuUnconditional
from gui.utils.clipboard import fromClipboard
from service.fit import Fit
from service.port.eft import parseAdditions
from service.port.eft import parseAdditions, importGetMutationData, lineIter
_t = wx.GetTranslation
@@ -41,9 +41,12 @@ class AdditionsImport(ContextMenuUnconditional):
def activate(self, callingWindow, fullContext, i):
text = fromClipboard()
items = parseAdditions(text)
lines = list(lineIter(text))
mutaData = importGetMutationData(lines)
text = '\n'.join(lines)
items = parseAdditions(text, mutaData=mutaData)
filterFunc = self.viewSpecMap[self.srcContext][1]
items = [(i.ID, a) for i, a in items if filterFunc(i)]
items = [(i.ID, a, m) for i, a, m in items if filterFunc(i)]
if not items:
return
command = self.viewSpecMap[self.srcContext][2]

View File

@@ -8,7 +8,7 @@ from eos.saveddata.cargo import Cargo as es_Cargo
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter as es_Fighter
from eos.saveddata.fit import Fit as es_Fit
from gui.contextMenu import ContextMenuSingle
from gui.contextMenu import ContextMenuCombined
from service.fit import Fit
# noinspection PyPackageRequirements
@@ -16,12 +16,12 @@ from service.fit import Fit
_t = wx.GetTranslation
class ChangeItemAmount(ContextMenuSingle):
class ChangeItemAmount(ContextMenuCombined):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, callingWindow, srcContext, mainItem):
def display(self, callingWindow, srcContext, mainItem, selection):
if srcContext not in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter"):
return False
@@ -30,10 +30,12 @@ class ChangeItemAmount(ContextMenuSingle):
return True
def getText(self, callingWindow, itmContext, mainItem):
def getText(self, callingWindow, itmContext, mainItem, selection):
if isinstance(mainItem, es_Cargo):
return _t("Change Selection Quantity")
return _t("Change {0} Quantity").format(itmContext)
def activate(self, callingWindow, fullContext, mainItem, i):
def activate(self, callingWindow, fullContext, mainItem, selection, i):
fitID = self.mainFrame.getActiveFit()
srcContext = fullContext[0]
if isinstance(mainItem, es_Fit):
@@ -56,8 +58,12 @@ class ChangeItemAmount(ContextMenuSingle):
cleanInput = int(float(re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())))
if isinstance(mainItem, es_Cargo):
self.mainFrame.command.Submit(cmd.GuiChangeCargoAmountCommand(
fitID=fitID, itemID=mainItem.itemID, amount=cleanInput))
itemIDs = []
for cargo in selection:
if cargo in fit.cargo:
itemIDs.append(cargo.itemID)
self.mainFrame.command.Submit(cmd.GuiChangeCargosAmountCommand(
fitID=fitID, itemIDs=itemIDs, amount=cleanInput))
elif isinstance(mainItem, Drone):
if srcContext == "projectedDrone":
self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneAmountCommand(

View File

@@ -1,16 +1,19 @@
# noinspection PyPackageRequirements
import re
# noinspection PyPackageRequirements
import wx
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from gui.fitCommands import GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand
from gui.fitCommands import (
GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand,
GuiConvertMutatedLocalDroneCommand, GuiRevertMutatedLocalDroneCommand)
from service.fit import Fit
_t = wx.GetTranslation
class ChangeModuleMutation(ContextMenuSingle):
class ChangeItemMutation(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -18,10 +21,10 @@ class ChangeModuleMutation(ContextMenuSingle):
def display(self, callingWindow, srcContext, mainItem):
if srcContext != "fittingModule" or self.mainFrame.getActiveFit() is None:
if srcContext not in ("fittingModule", "droneItem") or self.mainFrame.getActiveFit() is None:
return False
if mainItem is None or mainItem.isEmpty:
if mainItem is None or getattr(mainItem, 'isEmpty', False):
return False
if len(mainItem.item.mutaplasmids) == 0 and not mainItem.isMutated:
@@ -42,24 +45,27 @@ class ChangeModuleMutation(ContextMenuSingle):
menu = rootMenu if msw else sub
for item in mainItem.item.mutaplasmids:
label = item.item.name
for mutaplasmid in mainItem.item.mutaplasmids:
id = ContextMenuSingle.nextID()
self.eventIDs[id] = (item, mainItem)
skillItem = wx.MenuItem(menu, id, label)
menu.Bind(wx.EVT_MENU, self.handleMenu, skillItem)
sub.Append(skillItem)
self.eventIDs[id] = (mutaplasmid, mainItem)
mItem = wx.MenuItem(menu, id, mutaplasmid.shortName)
menu.Bind(wx.EVT_MENU, self.handleMenu, mItem)
sub.Append(mItem)
return sub
def handleMenu(self, event):
mutaplasmid, mod = self.eventIDs[event.Id]
mutaplasmid, item = self.eventIDs[event.Id]
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
if mod in fit.modules:
position = fit.modules.index(mod)
if item in fit.modules:
position = fit.modules.index(item)
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
elif item in fit.drones:
position = fit.drones.index(item)
self.mainFrame.command.Submit(GuiConvertMutatedLocalDroneCommand(
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
def activate(self, callingWindow, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
@@ -68,9 +74,13 @@ class ChangeModuleMutation(ContextMenuSingle):
position = fit.modules.index(mainItem)
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
fitID=fitID, position=position))
elif mainItem in fit.drones:
position = fit.drones.index(mainItem)
self.mainFrame.command.Submit(GuiRevertMutatedLocalDroneCommand(
fitID=fitID, position=position))
def getBitmap(self, callingWindow, context, mainItem):
return None
ChangeModuleMutation.register()
ChangeItemMutation.register()

View File

@@ -355,4 +355,5 @@ class ItemParams(wx.Panel):
fvalue = roundDec(value, digits)
else:
fvalue = value
return "%s %s" % (fvalue, unit)
unitSuffix = f' {unit}' if unit is not None else ''
return f'{fvalue}{unitSuffix}'

View File

@@ -18,23 +18,22 @@ _t = wx.GetTranslation
class ItemMutatorPanel(wx.Panel):
def __init__(self, parent, mod):
def __init__(self, parent, stuff):
wx.Panel.__init__(self, parent)
self.stuff = mod
self.stuff = stuff
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
mainSizer = wx.BoxSizer(wx.VERTICAL)
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
headerSizer.AddStretchSpacer()
itemIcon = BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons")
itemIcon = BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons")
if itemIcon is not None:
headerSizer.Add(itemIcon, 0, 0, 0)
mutaIcon = BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons")
mutaIcon = BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons")
if mutaIcon is not None:
headerSizer.Add(mutaIcon, 0, wx.LEFT, 0)
sourceItemShort = "{} {}".format(mod.mutaplasmid.item.name.split(" ")[0], mod.baseItem.name)
sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
sourceItemText = wx.StaticText(self, wx.ID_ANY, stuff.fullName)
font = parent.GetFont()
font.SetWeight(wx.BOLD)
sourceItemText.SetFont(font)
@@ -43,7 +42,7 @@ class ItemMutatorPanel(wx.Panel):
mainSizer.Add(headerSizer, 0, wx.ALL | wx.EXPAND, 5)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
self.mutaList = ItemMutatorList(self, mod)
self.mutaList = ItemMutatorList(self, stuff)
mainSizer.Add(self.mutaList, 1, wx.EXPAND | wx.ALL, 0)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
@@ -68,13 +67,13 @@ class ItemMutatorPanel(wx.Panel):
class ItemMutatorList(wx.ScrolledWindow):
def __init__(self, parent, mod):
def __init__(self, parent, stuff):
wx.ScrolledWindow.__init__(self, parent)
self.SetScrollRate(0, 15)
self.carryingFitID = gui.mainFrame.MainFrame.getInstance().getActiveFit()
self.initialMutations = {}
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
self.mod = mod
self.stuff = stuff
self.timer = None
self.isModified = False
@@ -91,9 +90,8 @@ class ItemMutatorList(wx.ScrolledWindow):
('Damage Control', 'duration'): True,
('Siege Module', 'siegeLocalLogisticsDurationBonus'): False
}
first = True
for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
if m.baseValue == 0:
continue
if not first:
@@ -102,10 +100,10 @@ class ItemMutatorList(wx.ScrolledWindow):
self.initialMutations[m.attrID] = m.value
highIsGood = higOverrides.get((mod.item.group.name, m.attribute.name), m.highIsGood)
highIsGood = higOverrides.get((stuff.item.group.name, m.attribute.name), m.highIsGood)
# Format: [raw value, modifier applied to base raw value, display value]
range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue))
range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue))
range1 = (m.minValue, self._simplifyValue(m, m.minValue))
range2 = (m.maxValue, self._simplifyValue(m, m.maxValue))
# minValue/maxValue do not always correspond to min/max, because these are
# just base value multiplied by minMod/maxMod, and in case base is negative
@@ -148,11 +146,11 @@ class ItemMutatorList(wx.ScrolledWindow):
headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0)
worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
worseVal = ItemParams.FormatValue(*self._preformatValue(m, worseRange[0]), rounding='dec')
worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
worseText.SetForegroundColour(badColor)
betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
betterVal = ItemParams.FormatValue(*self._preformatValue(m, betterRange[0]), rounding='dec')
betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
betterText.SetForegroundColour(goodColor)
@@ -163,23 +161,38 @@ class ItemMutatorList(wx.ScrolledWindow):
sizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
slider = AttributeSlider(parent=self,
baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
baseValue=self._simplifyValue(m, sliderBaseValue),
minValue=displayMinRange[1],
maxValue=displayMaxRange[1],
inverse=displayMaxRange is worseRange)
slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
slider.SetValue(self._simplifyValue(m, m.value), False)
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
self.event_mapping[slider] = m
sizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
self.SetSizer(sizer)
def _simplifyValue(self, mutator, value):
if mutator.attribute.unit is None:
return value
return mutator.attribute.unit.SimplifyValue(value)
def _complicateValue(self, mutator, value):
if mutator.attribute.unit is None:
return value
return mutator.attribute.unit.ComplicateValue(value)
def _preformatValue(self, mutator, value):
if mutator.attribute.unit is None:
return value, None
return mutator.attribute.unit.PreformatValue(value)
def changeMutatedValue(self, evt):
if evt.AffectsModifiedFlag:
self.isModified = True
m = self.event_mapping[evt.Object]
value = evt.Value
value = m.attribute.unit.ComplicateValue(value)
value = self._complicateValue(m, value)
sFit = Fit.getInstance()
sFit.changeMutatedValuePrelim(m, value)
@@ -198,7 +211,7 @@ class ItemMutatorList(wx.ScrolledWindow):
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
value = m.attribute.unit.SimplifyValue(value)
value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -208,7 +221,7 @@ class ItemMutatorList(wx.ScrolledWindow):
for slider, m in self.event_mapping.items():
value = random.uniform(m.minValue, m.maxValue)
value = sFit.changeMutatedValuePrelim(m, value)
value = m.attribute.unit.SimplifyValue(value)
value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -218,7 +231,7 @@ class ItemMutatorList(wx.ScrolledWindow):
for slider, m in self.event_mapping.items():
if m.attrID in self.initialMutations:
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
value = m.attribute.unit.SimplifyValue(value)
value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -226,25 +239,34 @@ class ItemMutatorList(wx.ScrolledWindow):
# Submit mutation changes
sFit = Fit.getInstance()
fit = sFit.getFit(self.carryingFitID)
if self.mod in fit.modules:
isCurrentMod = self.stuff in fit.modules
isCurrentDrone = self.stuff in fit.drones
if isCurrentMod or isCurrentDrone:
if self.isModified:
currentMutation = {}
for slider, m in self.event_mapping.items():
# Sliders may have more up-to-date info than mutator in case we changed
# value in slider and without confirming it, decided to close window
value = slider.GetValue()
value = m.attribute.unit.ComplicateValue(value)
value = self._complicateValue(m, value)
if value != m.value:
value = sFit.changeMutatedValuePrelim(m, value)
currentMutation[m.attrID] = value
else:
currentMutation = self.initialMutations
mainFrame = gui.mainFrame.MainFrame.getInstance()
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
fitID=self.carryingFitID,
position=fit.modules.index(self.mod),
mutation=currentMutation,
oldMutation=self.initialMutations))
if isCurrentMod:
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
fitID=self.carryingFitID,
position=fit.modules.index(self.stuff),
mutation=currentMutation,
oldMutation=self.initialMutations))
elif isCurrentDrone:
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalDroneMutationCommand(
fitID=self.carryingFitID,
position=fit.drones.index(self.stuff),
mutation=currentMutation,
oldMutation=self.initialMutations))
for slider in self.event_mapping:
slider.OnWindowClose()

View File

@@ -70,7 +70,7 @@ class BaseName(ViewColumn):
stuff = stuff.item
if isinstance(stuff, Drone):
return "%dx %s" % (stuff.amount, stuff.item.name)
return "%dx %s" % (stuff.amount, stuff.fullName)
elif isinstance(stuff, Fighter):
return "%d/%d %s" % \
(stuff.amount, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name)
@@ -117,7 +117,7 @@ class BaseName(ViewColumn):
if stuff.isEmpty:
return "%s Slot" % FittingSlot(stuff.slot).name.capitalize()
else:
return stuff.item.customName
return stuff.fullName
elif isinstance(stuff, Implant):
return stuff.item.name
elif isinstance(stuff, TargetProfile):

View File

@@ -5,7 +5,7 @@ from .gui.booster.remove import GuiRemoveBoostersCommand
from .gui.booster.sideEffectToggleState import GuiToggleBoosterSideEffectStateCommand
from .gui.booster.toggleStates import GuiToggleBoosterStatesCommand
from .gui.cargo.add import GuiAddCargoCommand
from .gui.cargo.changeAmount import GuiChangeCargoAmountCommand
from .gui.cargo.changeAmount import GuiChangeCargosAmountCommand
from .gui.cargo.changeMetas import GuiChangeCargoMetasCommand
from .gui.cargo.imprt import GuiImportCargosCommand
from .gui.cargo.remove import GuiRemoveCargosCommand
@@ -26,8 +26,12 @@ from .gui.itemsRebase import GuiRebaseItemsCommand
from .gui.localDrone.add import GuiAddLocalDroneCommand
from .gui.localDrone.changeAmount import GuiChangeLocalDroneAmountCommand
from .gui.localDrone.changeMetas import GuiChangeLocalDroneMetasCommand
from .gui.localDrone.changeMutation import GuiChangeLocalDroneMutationCommand
from .gui.localDrone.clone import GuiCloneLocalDroneCommand
from .gui.localDrone.imprt import GuiImportLocalDronesCommand
from .gui.localDrone.mutatedConvert import GuiConvertMutatedLocalDroneCommand
from .gui.localDrone.mutatedImport import GuiImportLocalMutatedDroneCommand
from .gui.localDrone.mutatedRevert import GuiRevertMutatedLocalDroneCommand
from .gui.localDrone.remove import GuiRemoveLocalDronesCommand
from .gui.localDrone.stackSplit import GuiSplitLocalDroneStackCommand
from .gui.localDrone.stacksMerge import GuiMergeLocalDroneStacksCommand

View File

@@ -0,0 +1,51 @@
import wx
from logbook import Logger
from service.fit import Fit
pyfalog = Logger(__name__)
class CalcChangeLocalDroneMutationCommand(wx.Command):
def __init__(self, fitID, position, mutation, oldMutation=None):
wx.Command.__init__(self, True, 'Change Local Drone Mutation')
self.fitID = fitID
self.position = position
self.mutation = mutation
self.savedMutation = oldMutation
def Do(self):
pyfalog.debug('Doing changing of local drone mutation at position {} to {} for fit ID {}'.format(
self.position, self.mutation, self.fitID))
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
drone = fit.drones[self.position]
if not drone.isMutated:
return False
if self.savedMutation is None:
self.savedMutation = {}
for mutator in drone.mutators.values():
self.savedMutation[mutator.attrID] = mutator.value
if self.mutation == self.savedMutation:
return False
for mutator in drone.mutators.values():
if mutator.attrID not in self.mutation:
continue
if mutator.value != self.mutation[mutator.attrID]:
mutator.value = self.mutation[mutator.attrID]
return True
def Undo(self):
pyfalog.debug('Undoing changing of local drone mutation at position {} to {} for fit ID {}'.format(
self.position, self.mutation, self.fitID))
cmd = CalcChangeLocalDroneMutationCommand(
fitID=self.fitID,
position=self.position,
mutation=self.savedMutation)
return cmd.Do()

View File

@@ -10,21 +10,26 @@ from gui.fitCommands.calc.cargo.remove import CalcRemoveCargoCommand
from gui.fitCommands.helpers import CargoInfo, InternalCommandHistory
class GuiChangeCargoAmountCommand(wx.Command):
class GuiChangeCargosAmountCommand(wx.Command):
def __init__(self, fitID, itemID, amount):
def __init__(self, fitID, itemIDs, amount):
wx.Command.__init__(self, True, 'Change Cargo Amount')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.itemID = itemID
self.itemIDs = itemIDs
self.amount = amount
def Do(self):
results = []
if self.amount > 0:
cmd = CalcChangeCargoAmountCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=self.itemID, amount=self.amount))
for itemID in self.itemIDs:
cmd = CalcChangeCargoAmountCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=itemID, amount=self.amount))
results.append(self.internalHistory.submit(cmd))
else:
cmd = CalcRemoveCargoCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=self.itemID, amount=math.inf))
success = self.internalHistory.submit(cmd)
for itemID in self.itemIDs:
cmd = CalcRemoveCargoCommand(fitID=self.fitID, cargoInfo=CargoInfo(itemID=itemID, amount=math.inf))
results.append(self.internalHistory.submit(cmd))
success = any(results)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success

View File

@@ -14,7 +14,7 @@ class GuiImportCargosCommand(wx.Command):
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.cargos = {}
for itemID, amount in cargos:
for itemID, amount, mutation in cargos:
if itemID not in self.cargos:
self.cargos[itemID] = 0
self.cargos[itemID] += amount

View File

@@ -0,0 +1,44 @@
import wx
import eos.db
import gui.mainFrame
from gui import globalEvents as GE
from gui.fitCommands.calc.drone.localChangeMutation import CalcChangeLocalDroneMutationCommand
from gui.fitCommands.helpers import InternalCommandHistory
from service.fit import Fit
class GuiChangeLocalDroneMutationCommand(wx.Command):
def __init__(self, fitID, position, mutation, oldMutation=None):
wx.Command.__init__(self, True, 'Change Local Drone Mutation')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.position = position
self.mutation = mutation
self.oldMutation = oldMutation
def Do(self):
cmd = CalcChangeLocalDroneMutationCommand(
fitID=self.fitID,
position=self.position,
mutation=self.mutation,
oldMutation=self.oldMutation)
success = self.internalHistory.submit(cmd)
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success
def Undo(self):
success = self.internalHistory.undoAll()
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success

View File

@@ -14,15 +14,24 @@ class GuiImportLocalDronesCommand(wx.Command):
wx.Command.__init__(self, True, 'Import Local Drones')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.drones = drones
self.droneInfos = []
for itemID, amount, mutation in drones:
if mutation:
mutaplasmid, attrs = mutation
self.droneInfos.append(DroneInfo(
itemID=mutaplasmid.resultingItem.ID,
amount=amount,
amountActive=0,
baseItemID=itemID,
mutaplasmidID=mutaplasmid.ID,
mutations=attrs))
else:
self.droneInfos.append(DroneInfo(itemID=itemID, amount=amount, amountActive=0))
def Do(self):
results = []
for itemID, amount in self.drones:
cmd = CalcAddLocalDroneCommand(
fitID=self.fitID,
droneInfo=DroneInfo(itemID=itemID, amount=amount, amountActive=0),
forceNewStack=True)
for info in self.droneInfos:
cmd = CalcAddLocalDroneCommand(fitID=self.fitID, droneInfo=info, forceNewStack=True)
results.append(self.internalHistory.submit(cmd))
success = any(results)
eos.db.flush()

View File

@@ -0,0 +1,65 @@
import math
import wx
import eos.db
import gui.mainFrame
from gui import globalEvents as GE
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
from service.fit import Fit
class GuiConvertMutatedLocalDroneCommand(wx.Command):
def __init__(self, fitID, position, mutaplasmid):
wx.Command.__init__(self, True, 'Convert Local Drone to Mutated')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.position = position
self.itemID = mutaplasmid.resultingItem.ID
self.mutaplasmidID = mutaplasmid.ID
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
try:
drone = fit.drones[self.position]
except IndexError:
return False
if drone.isMutated:
return False
info = DroneInfo(
amount=drone.amount,
amountActive=drone.amountActive,
itemID=self.itemID,
baseItemID=drone.item.ID,
mutaplasmidID=self.mutaplasmidID,
mutations={})
cmdRemove = CalcRemoveLocalDroneCommand(
fitID=self.fitID,
position=self.position,
amount=math.inf)
cmdAdd = CalcAddLocalDroneCommand(
fitID=self.fitID,
droneInfo=info,
forceNewStack=True,
ignoreRestrictions=True)
success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
eos.db.flush()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success
def Undo(self):
success = self.internalHistory.undoAll()
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success

View File

@@ -0,0 +1,44 @@
import wx
import eos.db
import gui.mainFrame
from gui import globalEvents as GE
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
from gui.fitCommands.helpers import InternalCommandHistory, DroneInfo
from service.fit import Fit
class GuiImportLocalMutatedDroneCommand(wx.Command):
def __init__(self, fitID, baseItem, mutaplasmid, mutations, amount):
wx.Command.__init__(self, True, 'Import Local Mutated Drone')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.newDroneInfo = DroneInfo(
amount=amount,
amountActive=0,
itemID=mutaplasmid.resultingItem.ID,
baseItemID=baseItem.ID,
mutaplasmidID=mutaplasmid.ID,
mutations=mutations)
def Do(self):
cmd = CalcAddLocalDroneCommand(fitID=self.fitID, droneInfo=self.newDroneInfo, forceNewStack=True)
success = self.internalHistory.submit(cmd)
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success
def Undo(self):
success = self.internalHistory.undoAll()
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success

View File

@@ -0,0 +1,60 @@
import math
import wx
import eos.db
import gui.mainFrame
from gui import globalEvents as GE
from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
from service.fit import Fit
class GuiRevertMutatedLocalDroneCommand(wx.Command):
def __init__(self, fitID, position):
wx.Command.__init__(self, True, 'Revert Local Drone from Mutated')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.position = position
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
try:
drone = fit.drones[self.position]
except IndexError:
return False
if not drone.isMutated:
return False
info = DroneInfo(
amount=drone.amount,
amountActive=drone.amountActive,
itemID=drone.baseItemID)
cmdRemove = CalcRemoveLocalDroneCommand(
fitID=self.fitID,
position=self.position,
amount=math.inf)
cmdAdd = CalcAddLocalDroneCommand(
fitID=self.fitID,
droneInfo=info,
forceNewStack=True,
ignoreRestrictions=True)
success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
eos.db.flush()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success
def Undo(self):
success = self.internalHistory.undoAll()
eos.db.flush()
sFit = Fit.getInstance()
sFit.recalc(self.fitID)
sFit.fill(self.fitID)
eos.db.commit()
wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
return success

View File

@@ -14,7 +14,7 @@ class GuiImportLocalFightersCommand(wx.Command):
wx.Command.__init__(self, True, 'Import Local Fighters')
self.internalHistory = InternalCommandHistory()
self.fitID = fitID
self.fighters = fighters
self.fighters = [(i, a) for i, a, m in fighters]
def Do(self):
results = []

View File

@@ -21,7 +21,10 @@ class GuiConvertMutatedLocalModuleCommand(wx.Command):
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
mod = fit.modules[self.position]
try:
mod = fit.modules[self.position]
except IndexError:
return False
if mod.isEmpty:
return False
if mod.isMutated:

View File

@@ -19,7 +19,10 @@ class GuiRevertMutatedLocalModuleCommand(wx.Command):
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
mod = fit.modules[self.position]
try:
mod = fit.modules[self.position]
except IndexError:
return False
if mod.isEmpty:
return False
if not mod.isMutated:

View File

@@ -158,8 +158,11 @@ class ModuleInfo:
class DroneInfo:
def __init__(self, itemID, amount, amountActive):
def __init__(self, amount, amountActive, itemID, baseItemID=None, mutaplasmidID=None, mutations=None):
self.itemID = itemID
self.baseItemID = baseItemID
self.mutaplasmidID = mutaplasmidID
self.mutations = mutations
self.amount = amount
self.amountActive = amountActive
@@ -170,22 +173,40 @@ class DroneInfo:
info = cls(
itemID=drone.itemID,
amount=drone.amount,
amountActive=drone.amountActive)
amountActive=drone.amountActive,
baseItemID=drone.baseItemID,
mutaplasmidID=drone.mutaplasmidID,
mutations={m.attrID: m.value for m in drone.mutators.values()})
return info
def toDrone(self):
item = Market.getInstance().getItem(self.itemID, eager=('attributes', 'group.category'))
mkt = Market.getInstance()
item = mkt.getItem(self.itemID, eager=('attributes', 'group.category'))
if self.baseItemID and self.mutaplasmidID:
baseItem = mkt.getItem(self.baseItemID, eager=('attributes', 'group.category'))
mutaplasmid = eos.db.getDynamicItem(self.mutaplasmidID)
else:
baseItem = None
mutaplasmid = None
try:
drone = Drone(item)
drone = Drone(item, baseItem=baseItem, mutaplasmid=mutaplasmid)
except ValueError:
pyfalog.warning('Invalid item: {}'.format(self.itemID))
return None
if self.mutations is not None:
for attrID, mutator in drone.mutators.items():
if attrID in self.mutations:
mutator.value = self.mutations[attrID]
drone.amount = self.amount
drone.amountActive = self.amountActive
return drone
def __repr__(self):
return makeReprStr(self, ['itemID', 'amount', 'amountActive'])
return makeReprStr(self, [
'itemID', 'amount', 'amountActive',
'baseItemID', 'mutaplasmidID', 'mutations'])
class FighterInfo:

View File

@@ -22,6 +22,7 @@ import wx
import config
import gui.mainFrame
from eos.saveddata.drone import Drone
from eos.saveddata.module import Module
from gui.auxWindow import AuxiliaryFrame
from gui.bitmap_loader import BitmapLoader
@@ -165,7 +166,7 @@ class ItemStatsContainer(wx.Panel):
self.traits = ItemTraits(self.nbContainer, stuff, item)
self.nbContainer.AddPage(self.traits, _t("Traits"))
if isinstance(stuff, Module) and stuff.isMutated:
if isinstance(stuff, (Module, Drone)) and stuff.isMutated:
self.mutator = ItemMutatorPanel(self.nbContainer, stuff)
self.nbContainer.AddPage(self.mutator, _t("Mutations"))

View File

@@ -762,28 +762,33 @@ class MainFrame(wx.Frame):
if importType == "FittingItem":
baseItem, mutaplasmidItem, mutations = importData[0]
if mutaplasmidItem:
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(activeFit, baseItem, mutaplasmidItem, mutations))
if baseItem.isDrone:
self.command.Submit(cmd.GuiImportLocalMutatedDroneCommand(
activeFit, baseItem, mutaplasmidItem, mutations, amount=1))
else:
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(
activeFit, baseItem, mutaplasmidItem, mutations))
else:
self.command.Submit(cmd.GuiAddLocalModuleCommand(activeFit, baseItem.ID))
return
if importType == "AdditionsDrones":
if self.command.Submit(cmd.GuiImportLocalDronesCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
if self.command.Submit(cmd.GuiImportLocalDronesCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
self.additionsPane.select("Drones")
return
if importType == "AdditionsFighters":
if self.command.Submit(cmd.GuiImportLocalFightersCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
if self.command.Submit(cmd.GuiImportLocalFightersCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
self.additionsPane.select("Fighters")
return
if importType == "AdditionsImplants":
if self.command.Submit(cmd.GuiImportImplantsCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
if self.command.Submit(cmd.GuiImportImplantsCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
self.additionsPane.select("Implants")
return
if importType == "AdditionsBoosters":
if self.command.Submit(cmd.GuiImportBoostersCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
if self.command.Submit(cmd.GuiImportBoostersCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
self.additionsPane.select("Boosters")
return
if importType == "AdditionsCargo":
if self.command.Submit(cmd.GuiImportCargosCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
if self.command.Submit(cmd.GuiImportCargosCommand(activeFit, [(i.ID, a, m) for i, a, m in importData[0]])):
self.additionsPane.select("Cargo")
return
except (KeyboardInterrupt, SystemExit):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

BIN
imgs/icons/24776@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

BIN
imgs/icons/24776@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
imgs/icons/24777@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

BIN
imgs/icons/24777@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/icons/24778@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

BIN
imgs/icons/24778@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
imgs/icons/24779@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

BIN
imgs/icons/24779@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/icons/24780@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

BIN
imgs/icons/24780@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
imgs/icons/24781@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

BIN
imgs/icons/24781@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24782@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

BIN
imgs/icons/24782@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
imgs/icons/24783@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

BIN
imgs/icons/24783@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24792@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

BIN
imgs/icons/24792@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
imgs/icons/24793@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

BIN
imgs/icons/24793@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
imgs/icons/24794@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

BIN
imgs/icons/24794@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
imgs/icons/24795@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
imgs/icons/24795@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
imgs/icons/24796@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

BIN
imgs/icons/24796@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/24797@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

BIN
imgs/icons/24797@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/24798@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

BIN
imgs/icons/24798@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/24799@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

BIN
imgs/icons/24799@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24800@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

BIN
imgs/icons/24800@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24801@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

BIN
imgs/icons/24801@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24802@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

BIN
imgs/icons/24802@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24803@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

BIN
imgs/icons/24803@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24804@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
imgs/icons/24804@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24805@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
imgs/icons/24805@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24806@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

BIN
imgs/icons/24806@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24807@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

BIN
imgs/icons/24807@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24808@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

BIN
imgs/icons/24808@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/24815@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

BIN
imgs/icons/24815@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/24816@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

BIN
imgs/icons/24816@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
imgs/icons/24817@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

BIN
imgs/icons/24817@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/24818@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

BIN
imgs/icons/24818@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/icons/24819@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

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