Merge remote-tracking branch 'origin/projections'
This commit is contained in:
16
eos/db/migrations/upgrade10.py
Normal file
16
eos/db/migrations/upgrade10.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Migration 10
|
||||
|
||||
- Adds active attribute to projected fits
|
||||
"""
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Update projectedFits schema to include active attribute
|
||||
try:
|
||||
saveddata_engine.execute("SELECT active FROM projectedFits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE projectedFits ADD COLUMN active BOOLEAN")
|
||||
saveddata_engine.execute("UPDATE projectedFits SET active = 1")
|
||||
saveddata_engine.execute("UPDATE projectedFits SET amount = 1")
|
||||
@@ -17,9 +17,11 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
#===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String, Boolean
|
||||
from sqlalchemy.orm import relation, mapper
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import *
|
||||
from sqlalchemy.sql import and_
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.db.saveddata.module import modules_table
|
||||
@@ -45,33 +47,119 @@ fits_table = Table("fits", saveddata_meta,
|
||||
projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||
Column("sourceID", ForeignKey("fits.ID"), primary_key = True),
|
||||
Column("victimID", ForeignKey("fits.ID"), primary_key = True),
|
||||
Column("amount", Integer))
|
||||
Column("amount", Integer, nullable = False, default = 1),
|
||||
Column("active", Boolean, nullable = False, default = 1),
|
||||
)
|
||||
|
||||
class ProjectedFit(object):
|
||||
def __init__(self, sourceID, source_fit, amount=1, active=True):
|
||||
self.sourceID = sourceID
|
||||
self.source_fit = source_fit
|
||||
self.active = active
|
||||
self.__amount = amount
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
if self.source_fit.isInvalid:
|
||||
# Very rare for this to happen, but be prepared for it
|
||||
eos.db.saveddata_session.delete(self.source_fit)
|
||||
eos.db.saveddata_session.flush()
|
||||
eos.db.saveddata_session.refresh(self.victim_fit)
|
||||
|
||||
# We have a series of setters and getters here just in case someone
|
||||
# downgrades and screws up the table with NULL values
|
||||
@property
|
||||
def amount(self):
|
||||
return self.__amount or 1
|
||||
|
||||
@amount.setter
|
||||
def amount(self, amount):
|
||||
self.__amount = amount
|
||||
|
||||
def __repr__(self):
|
||||
return "ProjectedFit(sourceID={}, victimID={}, amount={}, active={}) at {}".format(
|
||||
self.sourceID, self.victimID, self.amount, self.active, hex(id(self))
|
||||
)
|
||||
|
||||
Fit._Fit__projectedFits = association_proxy(
|
||||
"victimOf", # look at the victimOf association...
|
||||
"source_fit", # .. and return the source fits
|
||||
creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit)
|
||||
)
|
||||
|
||||
mapper(Fit, fits_table,
|
||||
properties = {"_Fit__modules" : relation(Module, collection_class = HandledModuleList,
|
||||
primaryjoin = and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False),
|
||||
order_by = modules_table.c.position, cascade='all, delete, delete-orphan'),
|
||||
"_Fit__projectedModules" : relation(Module, collection_class = HandledProjectedModList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
primaryjoin = and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)),
|
||||
"owner" : relation(User, backref = "fits"),
|
||||
"itemID" : fits_table.c.shipID,
|
||||
"shipID" : fits_table.c.shipID,
|
||||
"_Fit__boosters" : relation(Booster, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True),
|
||||
"_Fit__drones" : relation(Drone, collection_class = HandledDroneCargoList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
|
||||
"_Fit__cargo" : relation(Cargo, collection_class = HandledDroneCargoList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
primaryjoin = and_(cargo_table.c.fitID == fits_table.c.ID)),
|
||||
"_Fit__projectedDrones" : relation(Drone, collection_class = HandledProjectedDroneList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
|
||||
"_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', backref='fit', single_parent=True,
|
||||
primaryjoin = fitImplants_table.c.fitID == fits_table.c.ID,
|
||||
secondaryjoin = fitImplants_table.c.implantID == Implant.ID,
|
||||
secondary = fitImplants_table),
|
||||
"_Fit__character" : relation(Character, backref = "fits"),
|
||||
"_Fit__damagePattern" : relation(DamagePattern),
|
||||
"_Fit__targetResists" : relation(TargetResists),
|
||||
"_Fit__projectedFits" : relation(Fit,
|
||||
primaryjoin = projectedFits_table.c.victimID == fits_table.c.ID,
|
||||
secondaryjoin = fits_table.c.ID == projectedFits_table.c.sourceID,
|
||||
secondary = projectedFits_table,
|
||||
collection_class = HandledProjectedFitList)
|
||||
})
|
||||
properties = {
|
||||
"_Fit__modules": relation(
|
||||
Module,
|
||||
collection_class=HandledModuleList,
|
||||
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False),
|
||||
order_by=modules_table.c.position,
|
||||
cascade='all, delete, delete-orphan'),
|
||||
"_Fit__projectedModules": relation(
|
||||
Module,
|
||||
collection_class=HandledProjectedModList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)),
|
||||
"owner": relation(
|
||||
User,
|
||||
backref="fits"),
|
||||
"itemID": fits_table.c.shipID,
|
||||
"shipID": fits_table.c.shipID,
|
||||
"_Fit__boosters": relation(
|
||||
Booster,
|
||||
collection_class=HandledImplantBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True),
|
||||
"_Fit__drones": relation(
|
||||
Drone,
|
||||
collection_class=HandledDroneCargoList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
|
||||
"_Fit__cargo": relation(
|
||||
Cargo,
|
||||
collection_class=HandledDroneCargoList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)),
|
||||
"_Fit__projectedDrones": relation(
|
||||
Drone,
|
||||
collection_class=HandledProjectedDroneList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
single_parent=True,
|
||||
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
|
||||
"_Fit__implants": relation(
|
||||
Implant,
|
||||
collection_class=HandledImplantBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
backref='fit',
|
||||
single_parent=True,
|
||||
primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID,
|
||||
secondaryjoin=fitImplants_table.c.implantID == Implant.ID,
|
||||
secondary=fitImplants_table),
|
||||
"_Fit__character": relation(
|
||||
Character,
|
||||
backref="fits"),
|
||||
"_Fit__damagePattern": relation(DamagePattern),
|
||||
"_Fit__targetResists": relation(TargetResists),
|
||||
"projectedOnto": relationship(
|
||||
ProjectedFit,
|
||||
primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID,
|
||||
backref='source_fit',
|
||||
collection_class=attribute_mapped_collection('victimID'),
|
||||
cascade='all, delete, delete-orphan'),
|
||||
"victimOf": relationship(
|
||||
ProjectedFit,
|
||||
primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID,
|
||||
backref='victim_fit',
|
||||
collection_class=attribute_mapped_collection('sourceID'),
|
||||
cascade='all, delete, delete-orphan'),
|
||||
}
|
||||
)
|
||||
|
||||
mapper(ProjectedFit, projectedFits_table,
|
||||
properties = {
|
||||
"_ProjectedFit__amount": projectedFits_table.c.amount,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -232,11 +232,6 @@ class HandledProjectedDroneList(HandledDroneCargoList):
|
||||
if proj.isInvalid or not proj.item.isType("projected"):
|
||||
self.remove(proj)
|
||||
|
||||
class HandledProjectedFitList(HandledList):
|
||||
def append(self, proj):
|
||||
proj.projected = True
|
||||
list.append(self, proj)
|
||||
|
||||
class HandledItem(object):
|
||||
def preAssignItemAttr(self, *args, **kwargs):
|
||||
self.itemModifiedAttributes.preAssign(*args, **kwargs)
|
||||
|
||||
@@ -217,13 +217,16 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
if attributeName not in self.__affectedBy:
|
||||
self.__affectedBy[attributeName] = {}
|
||||
affs = self.__affectedBy[attributeName]
|
||||
origin = self.fit.getOrigin()
|
||||
fit = origin if origin and origin != self.fit else self.fit
|
||||
# If there's no set for current fit in dictionary, create it
|
||||
if self.fit not in affs:
|
||||
affs[self.fit] = []
|
||||
if fit not in affs:
|
||||
affs[fit] = []
|
||||
# Reassign alias to list
|
||||
affs = affs[self.fit]
|
||||
affs = affs[fit]
|
||||
# Get modifier which helps to compose 'Affected by' map
|
||||
modifier = self.fit.getModifier()
|
||||
|
||||
# Add current affliction to list
|
||||
affs.append((modifier, operation, bonus, used))
|
||||
|
||||
|
||||
@@ -286,5 +286,10 @@ class Skill(HandledItem):
|
||||
copy = Skill(self.item, self.level, self.__ro)
|
||||
return copy
|
||||
|
||||
def __repr__(self):
|
||||
return "Skill(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
|
||||
class ReadOnlyException(Exception):
|
||||
pass
|
||||
|
||||
@@ -29,6 +29,9 @@ from eos.saveddata.module import State
|
||||
from eos.saveddata.mode import Mode
|
||||
import eos.db
|
||||
import time
|
||||
import copy
|
||||
from utils.timer import Timer
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -40,14 +43,6 @@ except ImportError:
|
||||
|
||||
class Fit(object):
|
||||
"""Represents a fitting, with modules, ship, implants, etc."""
|
||||
EXTRA_ATTRIBUTES = {"armorRepair": 0,
|
||||
"hullRepair": 0,
|
||||
"shieldRepair": 0,
|
||||
"maxActiveDrones": 0,
|
||||
"maxTargetsLockedFromSkills": 2,
|
||||
"droneControlRange": 20000,
|
||||
"cloaked": False,
|
||||
"siege": False}
|
||||
|
||||
PEAK_RECHARGE = 0.25
|
||||
|
||||
@@ -61,7 +56,7 @@ class Fit(object):
|
||||
self.__cargo = HandledDroneCargoList()
|
||||
self.__implants = HandledImplantBoosterList()
|
||||
self.__boosters = HandledImplantBoosterList()
|
||||
self.__projectedFits = HandledProjectedFitList()
|
||||
#self.__projectedFits = {}
|
||||
self.__projectedModules = HandledProjectedModList()
|
||||
self.__projectedDrones = HandledProjectedDroneList()
|
||||
self.__character = None
|
||||
@@ -88,6 +83,10 @@ class Fit(object):
|
||||
|
||||
try:
|
||||
self.__ship = Ship(item)
|
||||
# @todo extra attributes is now useless, however it set to be
|
||||
# the same as ship attributes for ease (so we don't have to
|
||||
# change all instances in source). Remove this at some point
|
||||
self.extraAttributes = self.__ship.itemModifiedAttributes
|
||||
except ValueError:
|
||||
logger.error("Item (id: %d) is not a Ship", self.shipID)
|
||||
return
|
||||
@@ -124,8 +123,6 @@ class Fit(object):
|
||||
self.boostsFits = set()
|
||||
self.gangBoosts = None
|
||||
self.ecmProjectedStr = 1
|
||||
self.extraAttributes = ModifiedAttributeDict(self)
|
||||
self.extraAttributes.original = self.EXTRA_ATTRIBUTES
|
||||
|
||||
@property
|
||||
def targetResists(self):
|
||||
@@ -178,8 +175,11 @@ class Fit(object):
|
||||
def ship(self, ship):
|
||||
self.__ship = ship
|
||||
self.shipID = ship.item.ID if ship is not None else None
|
||||
# set mode of new ship
|
||||
self.mode = self.ship.validateModeItem(None) if ship is not None else None
|
||||
if ship is not None:
|
||||
# set mode of new ship
|
||||
self.mode = self.ship.validateModeItem(None) if ship is not None else None
|
||||
# set fit attributes the same as ship
|
||||
self.extraAttributes = self.ship.itemModifiedAttributes
|
||||
|
||||
@property
|
||||
def drones(self):
|
||||
@@ -207,7 +207,12 @@ class Fit(object):
|
||||
|
||||
@property
|
||||
def projectedFits(self):
|
||||
return self.__projectedFits
|
||||
# only in extreme edge cases will the fit be invalid, but to be sure do
|
||||
# not return them.
|
||||
return [fit for fit in self.__projectedFits.values() if not fit.isInvalid]
|
||||
|
||||
def getProjectionInfo(self, fitID):
|
||||
return self.projectedOnto.get(fitID, None)
|
||||
|
||||
@property
|
||||
def projectedDrones(self):
|
||||
@@ -326,7 +331,7 @@ class Fit(object):
|
||||
if map[key](val) == False: raise ValueError(str(val) + " is not a valid value for " + key)
|
||||
else: return val
|
||||
|
||||
def clear(self):
|
||||
def clear(self, projected=False):
|
||||
self.__effectiveTank = None
|
||||
self.__weaponDPS = None
|
||||
self.__minerYield = None
|
||||
@@ -346,15 +351,36 @@ class Fit(object):
|
||||
del self.__calculatedTargets[:]
|
||||
del self.__extraDrains[:]
|
||||
|
||||
if self.ship is not None: self.ship.clear()
|
||||
c = chain(self.modules, self.drones, self.boosters, self.implants, self.projectedDrones, self.projectedModules, self.projectedFits, (self.character, self.extraAttributes))
|
||||
if self.ship:
|
||||
self.ship.clear()
|
||||
|
||||
c = chain(
|
||||
self.modules,
|
||||
self.drones,
|
||||
self.boosters,
|
||||
self.implants,
|
||||
self.projectedDrones,
|
||||
self.projectedModules,
|
||||
(self.character, self.extraAttributes),
|
||||
)
|
||||
|
||||
for stuff in c:
|
||||
if stuff is not None and stuff != self: stuff.clear()
|
||||
if stuff is not None and stuff != self:
|
||||
stuff.clear()
|
||||
|
||||
# If this is the active fit that we are clearing, not a projected fit,
|
||||
# then this will run and clear the projected ships and flag the next
|
||||
# iteration to skip this part to prevent recursion.
|
||||
if not projected:
|
||||
for stuff in self.projectedFits:
|
||||
if stuff is not None and stuff != self:
|
||||
stuff.clear(projected=True)
|
||||
|
||||
#Methods to register and get the thing currently affecting the fit,
|
||||
#so we can correctly map "Affected By"
|
||||
def register(self, currModifier):
|
||||
def register(self, currModifier, origin=None):
|
||||
self.__modifier = currModifier
|
||||
self.__origin = origin
|
||||
if hasattr(currModifier, "itemModifiedAttributes"):
|
||||
currModifier.itemModifiedAttributes.fit = self
|
||||
if hasattr(currModifier, "chargeModifiedAttributes"):
|
||||
@@ -363,98 +389,129 @@ class Fit(object):
|
||||
def getModifier(self):
|
||||
return self.__modifier
|
||||
|
||||
def getOrigin(self):
|
||||
return self.__origin
|
||||
|
||||
def __calculateGangBoosts(self, runTime):
|
||||
logger.debug("Applying gang boosts in `%s` runtime for %s", runTime, repr(self))
|
||||
for name, info in self.gangBoosts.iteritems():
|
||||
# Unpack all data required to run effect properly
|
||||
effect, thing = info[1]
|
||||
if effect.runTime == runTime:
|
||||
context = ("gang", thing.__class__.__name__.lower())
|
||||
if isinstance(thing, Module):
|
||||
if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
|
||||
(effect.isType("active") and thing.state >= State.ACTIVE):
|
||||
# Run effect, and get proper bonuses applied
|
||||
try:
|
||||
self.register(thing)
|
||||
effect.handler(self, thing, context)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# Run effect, and get proper bonuses applied
|
||||
try:
|
||||
self.register(thing)
|
||||
effect.handler(self, thing, context)
|
||||
except:
|
||||
pass
|
||||
|
||||
def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None):
|
||||
refreshBoosts = False
|
||||
if withBoosters is True:
|
||||
refreshBoosts = True
|
||||
if dirtyStorage is not None and self.ID in dirtyStorage:
|
||||
refreshBoosts = True
|
||||
if dirtyStorage is not None:
|
||||
dirtyStorage.update(self.boostsFits)
|
||||
if self.fleet is not None and refreshBoosts is True:
|
||||
self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters, dirtyStorage=dirtyStorage)
|
||||
timer = Timer('Fit: {}, {}'.format(self.ID, self.name), logger)
|
||||
logger.debug("Starting fit calculation on: %s, withBoosters: %s", repr(self), withBoosters)
|
||||
|
||||
shadow = False
|
||||
if targetFit:
|
||||
logger.debug("Applying projections to target: %s", repr(targetFit))
|
||||
projectionInfo = self.getProjectionInfo(targetFit.ID)
|
||||
logger.debug("ProjectionInfo: %s", projectionInfo)
|
||||
if self == targetFit:
|
||||
copied = self # original fit
|
||||
shadow = True
|
||||
self = copy.deepcopy(self)
|
||||
self.fleet = copied.fleet
|
||||
logger.debug("Handling self projection - making shadow copy of fit. %s => %s", repr(copied), repr(self))
|
||||
# we delete the fit because when we copy a fit, flush() is
|
||||
# called to properly handle projection updates. However, we do
|
||||
# not want to save this fit to the database, so simply remove it
|
||||
eos.db.saveddata_session.delete(self)
|
||||
|
||||
if self.fleet is not None and withBoosters is True:
|
||||
logger.debug("Fleet is set, gathering gang boosts")
|
||||
self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters)
|
||||
timer.checkpoint("Done calculating gang boosts for %s"%repr(self))
|
||||
elif self.fleet is None:
|
||||
self.gangBoosts = None
|
||||
if dirtyStorage is not None:
|
||||
try:
|
||||
dirtyStorage.remove(self.ID)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# If we're not explicitly asked to project fit onto something,
|
||||
# set self as target fit
|
||||
if targetFit is None:
|
||||
targetFit = self
|
||||
forceProjected = False
|
||||
# Else, we're checking all target projectee fits
|
||||
elif targetFit not in self.__calculatedTargets:
|
||||
self.__calculatedTargets.append(targetFit)
|
||||
targetFit.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
|
||||
forceProjected = True
|
||||
# Or do nothing if target fit is calculated
|
||||
projected = False
|
||||
else:
|
||||
return
|
||||
projected = True
|
||||
|
||||
# If fit is calculated and we have nothing to do here, get out
|
||||
if self.__calculated == True and forceProjected == False:
|
||||
|
||||
# A note on why projected fits don't get to return here. If we return
|
||||
# here, the projection afflictions will not be run as they are
|
||||
# intertwined into the regular fit calculations. So, even if the fit has
|
||||
# been calculated, we need to recalculate it again just to apply the
|
||||
# projections. This is in contract to gang boosts, which are only
|
||||
# calculated once, and their items are then looped and accessed with
|
||||
# self.gangBoosts.iteritems()
|
||||
# We might be able to exit early in the fit calculations if we separate
|
||||
# projections from the normal fit calculations. But we must ensure that
|
||||
# projection have modifying stuff applied, such as gang boosts and other
|
||||
# local modules that may help
|
||||
if self.__calculated and not projected:
|
||||
logger.debug("Fit has already been calculated and is not projected, returning: %s", repr(self))
|
||||
return
|
||||
|
||||
# Mark fit as calculated
|
||||
self.__calculated = True
|
||||
|
||||
# There's a few things to keep in mind here
|
||||
# 1: Early effects first, then regular ones, then late ones, regardless of anything else
|
||||
# 2: Some effects aren't implemented
|
||||
# 3: Some effects are implemented poorly and will just explode on us
|
||||
# 4: Errors should be handled gracefully and preferably without crashing unless serious
|
||||
for runTime in ("early", "normal", "late"):
|
||||
# Build a little chain of stuff
|
||||
# Avoid adding projected drones and modules when fit is projected onto self
|
||||
# TODO: remove this workaround when proper self-projection using virtual duplicate fits is implemented
|
||||
if forceProjected is True:
|
||||
# if fit is being projected onto another fit
|
||||
c = chain((self.character, self.ship), self.drones, self.boosters, self.appliedImplants, self.modules)
|
||||
else:
|
||||
c = chain((self.character, self.ship, self.mode), self.drones, self.boosters, self.appliedImplants, self.modules,
|
||||
self.projectedDrones, self.projectedModules)
|
||||
c = chain(
|
||||
(self.character, self.ship),
|
||||
self.drones,
|
||||
self.boosters,
|
||||
self.appliedImplants,
|
||||
self.modules
|
||||
)
|
||||
|
||||
if not projected:
|
||||
# if not a projected fit, add a couple of more things
|
||||
c = chain(c, (self.mode,), self.projectedDrones, self.projectedModules)
|
||||
|
||||
# We calculate gang bonuses first so that projected fits get them
|
||||
if self.gangBoosts is not None:
|
||||
contextMap = {Skill: "skill",
|
||||
Ship: "ship",
|
||||
Module: "module",
|
||||
Implant: "implant"}
|
||||
for name, info in self.gangBoosts.iteritems():
|
||||
# Unpack all data required to run effect properly
|
||||
effect, thing = info[1]
|
||||
if effect.runTime == runTime:
|
||||
context = ("gang", contextMap[type(thing)])
|
||||
if isinstance(thing, Module):
|
||||
if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
|
||||
(effect.isType("active") and thing.state >= State.ACTIVE):
|
||||
# Run effect, and get proper bonuses applied
|
||||
try:
|
||||
self.register(thing)
|
||||
effect.handler(self, thing, context)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# Run effect, and get proper bonuses applied
|
||||
try:
|
||||
self.register(thing)
|
||||
effect.handler(self, thing, context)
|
||||
except:
|
||||
pass
|
||||
self.__calculateGangBoosts(runTime)
|
||||
|
||||
for item in c:
|
||||
# Registering the item about to affect the fit allows us to track "Affected By" relations correctly
|
||||
# Registering the item about to affect the fit allows us to
|
||||
# track "Affected By" relations correctly
|
||||
if item is not None:
|
||||
self.register(item)
|
||||
item.calculateModifiedAttributes(self, runTime, False)
|
||||
if forceProjected is True:
|
||||
targetFit.register(item)
|
||||
item.calculateModifiedAttributes(targetFit, runTime, True)
|
||||
if projected is True:
|
||||
for _ in xrange(projectionInfo.amount):
|
||||
targetFit.register(item, origin=self)
|
||||
item.calculateModifiedAttributes(targetFit, runTime, True)
|
||||
|
||||
for fit in self.projectedFits:
|
||||
fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
|
||||
timer.checkpoint('Done with runtime: %s'%runTime)
|
||||
|
||||
# Only apply projected fits if fit it not projected itself.
|
||||
if not projected:
|
||||
for fit in self.projectedFits:
|
||||
if fit.getProjectionInfo(self.ID).active:
|
||||
fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
|
||||
|
||||
timer.checkpoint('Done with fit calculation')
|
||||
|
||||
if shadow:
|
||||
logger.debug("Delete shadow fit object")
|
||||
del self
|
||||
|
||||
def fill(self):
|
||||
"""
|
||||
@@ -927,6 +984,19 @@ class Fit(object):
|
||||
c.append(deepcopy(i, memo))
|
||||
|
||||
for fit in self.projectedFits:
|
||||
copy.projectedFits.append(fit)
|
||||
copy.__projectedFits[fit.ID] = fit
|
||||
# this bit is required -- see GH issue # 83
|
||||
eos.db.saveddata_session.flush()
|
||||
eos.db.saveddata_session.refresh(fit)
|
||||
|
||||
return copy
|
||||
|
||||
def __repr__(self):
|
||||
return "Fit(ID={}, ship={}, name={}) at {}".format(
|
||||
self.ID, self.ship.item.name, self.name, hex(id(self))
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({})".format(
|
||||
self.name, self.ship.item.name
|
||||
)
|
||||
|
||||
@@ -639,6 +639,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
copy.state = self.state
|
||||
return copy
|
||||
|
||||
def __repr__(self):
|
||||
if self.item:
|
||||
return "Module(ID={}, name={}) at {}".format(
|
||||
self.item.ID, self.item.name, hex(id(self))
|
||||
)
|
||||
else:
|
||||
return "EmptyModule() at {}".format(hex(id(self)))
|
||||
|
||||
class Rack(Module):
|
||||
'''
|
||||
This is simply the Module class named something else to differentiate
|
||||
|
||||
@@ -26,6 +26,17 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Ship(ItemAttrShortcut, HandledItem):
|
||||
EXTRA_ATTRIBUTES = {
|
||||
"armorRepair": 0,
|
||||
"hullRepair": 0,
|
||||
"shieldRepair": 0,
|
||||
"maxActiveDrones": 0,
|
||||
"maxTargetsLockedFromSkills": 2,
|
||||
"droneControlRange": 20000,
|
||||
"cloaked": False,
|
||||
"siege": False
|
||||
}
|
||||
|
||||
def __init__(self, item):
|
||||
|
||||
if item.category.name != "Ship":
|
||||
@@ -34,7 +45,8 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
self.__item = item
|
||||
self.__modeItems = self.__getModeItems()
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
self.__itemModifiedAttributes.original = dict(self.item.attributes)
|
||||
self.__itemModifiedAttributes.original.update(self.EXTRA_ATTRIBUTES)
|
||||
|
||||
self.commandBonus = 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user