Merge branch 'development'
Conflicts: eos/db/saveddata/queries.py
This commit is contained in:
@@ -8,7 +8,6 @@ __all__ = [
|
||||
"booster",
|
||||
"drone",
|
||||
"implant",
|
||||
"fleet",
|
||||
"damagePattern",
|
||||
"miscData",
|
||||
"targetResists",
|
||||
|
||||
104
eos/db/saveddata/databaseRepair.py
Normal file
104
eos/db/saveddata/databaseRepair.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# ===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import sqlalchemy
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DatabaseCleanup:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def OrphanedCharacterSkills(saveddata_engine):
|
||||
# Finds and fixes database corruption issues.
|
||||
logger.debug("Start databsae validation and cleanup.")
|
||||
|
||||
# Find orphaned character skills.
|
||||
# This solves an issue where the character doesn't exist, but skills for that character do.
|
||||
# See issue #917
|
||||
try:
|
||||
logger.debug("Running database cleanup for character skills.")
|
||||
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills "
|
||||
"WHERE characterID NOT IN (SELECT ID from characters)")
|
||||
row = results.first()
|
||||
|
||||
if row and row['num']:
|
||||
delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)")
|
||||
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
|
||||
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
logger.error("Failed to connect to database.")
|
||||
|
||||
@staticmethod
|
||||
def OrphanedFitDamagePatterns(saveddata_engine):
|
||||
# Find orphaned damage patterns.
|
||||
# This solves an issue where the damage pattern doesn't exist, but fits reference the pattern.
|
||||
# See issue #777
|
||||
try:
|
||||
logger.debug("Running database cleanup for orphaned damage patterns attached to fits.")
|
||||
|
||||
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL")
|
||||
row = results.first()
|
||||
|
||||
if row and row['num']:
|
||||
# Get Uniform damage pattern ID
|
||||
query = saveddata_engine.execute("SELECT ID FROM damagePatterns WHERE name = 'Uniform'")
|
||||
rows = query.fetchall()
|
||||
|
||||
if len(rows) == 0:
|
||||
logger.error("Missing uniform damage pattern.")
|
||||
elif len(rows) > 1:
|
||||
logger.error("More than one uniform damage pattern found.")
|
||||
else:
|
||||
uniform_damage_pattern_id = rows[0]['ID']
|
||||
update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? "
|
||||
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL",
|
||||
uniform_damage_pattern_id)
|
||||
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
logger.error("Failed to connect to database.")
|
||||
|
||||
@staticmethod
|
||||
def OrphanedFitCharacterIDs(saveddata_engine):
|
||||
# Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern.
|
||||
try:
|
||||
logger.debug("Running database cleanup for orphaned characters attached to fits.")
|
||||
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL")
|
||||
row = results.first()
|
||||
|
||||
if row['num']:
|
||||
# Get All 5 character ID
|
||||
query = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'")
|
||||
rows = query.fetchall()
|
||||
|
||||
if len(rows) == 0:
|
||||
logger.error("Missing 'All 5' character.")
|
||||
elif len(rows) > 1:
|
||||
logger.error("More than one 'All 5' character found.")
|
||||
else:
|
||||
all5_id = rows[0]['ID']
|
||||
update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? "
|
||||
"WHERE characterID not in (select ID from characters) OR characterID IS NULL",
|
||||
all5_id)
|
||||
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
logger.error("Failed to connect to database.")
|
||||
@@ -1,65 +0,0 @@
|
||||
# ===============================================================================
|
||||
# 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/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.db.saveddata.fit import fits_table
|
||||
from eos.types import Fleet, Wing, Squad, Fit
|
||||
|
||||
gangs_table = Table("gangs", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("leaderID", ForeignKey("fits.ID")),
|
||||
Column("boosterID", ForeignKey("fits.ID")),
|
||||
Column("name", String))
|
||||
|
||||
wings_table = Table("wings", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("gangID", ForeignKey("gangs.ID")),
|
||||
Column("boosterID", ForeignKey("fits.ID")),
|
||||
Column("leaderID", ForeignKey("fits.ID")))
|
||||
|
||||
squads_table = Table("squads", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("wingID", ForeignKey("wings.ID")),
|
||||
Column("leaderID", ForeignKey("fits.ID")),
|
||||
Column("boosterID", ForeignKey("fits.ID")))
|
||||
|
||||
squadmembers_table = Table("squadmembers", saveddata_meta,
|
||||
Column("squadID", ForeignKey("squads.ID"), primary_key=True),
|
||||
Column("memberID", ForeignKey("fits.ID"), primary_key=True))
|
||||
|
||||
mapper(Fleet, gangs_table,
|
||||
properties={"wings": relation(Wing, backref="gang"),
|
||||
"leader": relation(Fit, primaryjoin=gangs_table.c.leaderID == fits_table.c.ID),
|
||||
"booster": relation(Fit, primaryjoin=gangs_table.c.boosterID == fits_table.c.ID)})
|
||||
|
||||
mapper(Wing, wings_table,
|
||||
properties={"squads": relation(Squad, backref="wing"),
|
||||
"leader": relation(Fit, primaryjoin=wings_table.c.leaderID == fits_table.c.ID),
|
||||
"booster": relation(Fit, primaryjoin=wings_table.c.boosterID == fits_table.c.ID)})
|
||||
|
||||
mapper(Squad, squads_table,
|
||||
properties={"leader": relation(Fit, primaryjoin=squads_table.c.leaderID == fits_table.c.ID),
|
||||
"booster": relation(Fit, primaryjoin=squads_table.c.boosterID == fits_table.c.ID),
|
||||
"members": relation(Fit,
|
||||
primaryjoin=squads_table.c.ID == squadmembers_table.c.squadID,
|
||||
secondaryjoin=squadmembers_table.c.memberID == fits_table.c.ID,
|
||||
secondary=squadmembers_table)})
|
||||
@@ -17,14 +17,12 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
from sqlalchemy.sql import and_
|
||||
|
||||
from eos.db import saveddata_session, sd_lock
|
||||
from eos.db.saveddata.fit import projectedFits_table
|
||||
from eos.db.saveddata.fleet import squadmembers_table
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.types import *
|
||||
from eos.db import saveddata_session, sd_lock
|
||||
|
||||
from eos.types import *
|
||||
from eos.db.saveddata.fit import projectedFits_table
|
||||
from sqlalchemy.sql import and_
|
||||
import eos.config
|
||||
|
||||
configVal = getattr(eos.config, "saveddataCache", None)
|
||||
@@ -212,52 +210,6 @@ def getFit(lookfor, eager=None):
|
||||
|
||||
return fit
|
||||
|
||||
|
||||
@cachedQuery(Fleet, 1, "fleetID")
|
||||
def getFleet(fleetID, eager=None):
|
||||
if isinstance(fleetID, int):
|
||||
if eager is None:
|
||||
with sd_lock:
|
||||
fleet = saveddata_session.query(Fleet).get(fleetID)
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
return fleet
|
||||
|
||||
|
||||
@cachedQuery(Wing, 1, "wingID")
|
||||
def getWing(wingID, eager=None):
|
||||
if isinstance(wingID, int):
|
||||
if eager is None:
|
||||
with sd_lock:
|
||||
wing = saveddata_session.query(Wing).get(wingID)
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
return wing
|
||||
|
||||
|
||||
@cachedQuery(Squad, 1, "squadID")
|
||||
def getSquad(squadID, eager=None):
|
||||
if isinstance(squadID, int):
|
||||
if eager is None:
|
||||
with sd_lock:
|
||||
squad = saveddata_session.query(Squad).get(squadID)
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
return squad
|
||||
|
||||
|
||||
def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
|
||||
"""
|
||||
Get all the fits using a certain ship.
|
||||
@@ -341,14 +293,6 @@ def getFitList(eager=None):
|
||||
|
||||
return fits
|
||||
|
||||
|
||||
def getFleetList(eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fleets = saveddata_session.query(Fleet).options(*eager).all()
|
||||
return fleets
|
||||
|
||||
|
||||
@cachedQuery(Price, 1, "typeID")
|
||||
def getPrice(typeID):
|
||||
if isinstance(typeID, int):
|
||||
@@ -472,18 +416,6 @@ def searchFits(nameLike, where=None, eager=None):
|
||||
|
||||
return fits
|
||||
|
||||
|
||||
def getSquadsIDsWithFitID(fitID):
|
||||
if isinstance(fitID, int):
|
||||
with sd_lock:
|
||||
squads = saveddata_session.query(squadmembers_table.c.squadID).filter(
|
||||
squadmembers_table.c.memberID == fitID).all()
|
||||
squads = tuple(entry[0] for entry in squads)
|
||||
return squads
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
|
||||
|
||||
def getProjectedFits(fitID):
|
||||
if isinstance(fitID, int):
|
||||
with sd_lock:
|
||||
|
||||
@@ -3,10 +3,28 @@
|
||||
# Used by:
|
||||
# Modules from group: Missile Launcher Heavy (12 of 12)
|
||||
# Modules from group: Missile Launcher Rocket (15 of 15)
|
||||
# Modules named like: Launcher (153 of 153)
|
||||
type = 'active'
|
||||
# Modules named like: Launcher (151 of 151)
|
||||
type = 'active', "projected"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
def handler(fit, src, context):
|
||||
# Set reload time to 10 seconds
|
||||
module.reloadTime = 10000
|
||||
src.reloadTime = 10000
|
||||
|
||||
if "projected" in context:
|
||||
if src.item.group.name == 'Missile Launcher Bomb':
|
||||
# Bomb Launcher Cooldown Timer
|
||||
moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay")
|
||||
|
||||
# Void and Focused Void Bombs
|
||||
neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount")
|
||||
|
||||
if moduleReactivationDelay and neutAmount:
|
||||
fit.addDrain(src, moduleReactivationDelay, neutAmount, 0)
|
||||
|
||||
# Lockbreaker Bombs
|
||||
ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType))
|
||||
|
||||
if ecmStrengthBonus:
|
||||
strModifier = 1 - ecmStrengthBonus / fit.scanStrength
|
||||
fit.ecmProjectedStr *= strModifier
|
||||
|
||||
@@ -134,7 +134,6 @@ class Fit(object):
|
||||
self.__capRecharge = None
|
||||
self.__calculatedTargets = []
|
||||
self.factorReload = False
|
||||
self.fleet = None
|
||||
self.boostsFits = set()
|
||||
self.gangBoosts = None
|
||||
self.ecmProjectedStr = 1
|
||||
@@ -632,14 +631,6 @@ class Fit(object):
|
||||
groups = ("Energy Weapon", "Hybrid Weapon")
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
|
||||
|
||||
# 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:
|
||||
@@ -665,7 +656,6 @@ class Fit(object):
|
||||
# Don't inspect this, we genuinely want to reassign self
|
||||
# noinspection PyMethodFirstArgAssignment
|
||||
self = copy.deepcopy(self)
|
||||
self.fleet = copied.fleet
|
||||
logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self)
|
||||
# we delete the fit because when we copy a fit, flush() is
|
||||
# called to properly handle projection updates. However, we do
|
||||
@@ -678,24 +668,6 @@ class Fit(object):
|
||||
continue
|
||||
|
||||
fit.calculateModifiedAttributes(self, True)
|
||||
#
|
||||
# for thing in chain(fit.modules, fit.implants, fit.character.skills, (fit.ship,)):
|
||||
# if thing.item is None:
|
||||
# continue
|
||||
# for effect in thing.item.effects.itervalues():
|
||||
# # And check if it actually has gang boosting effects
|
||||
# if effect.isType("gang"):
|
||||
# effect.handler(self, thing, ("commandRun"))
|
||||
|
||||
# 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 %r"%self)
|
||||
|
||||
# elif self.fleet is None:
|
||||
# self.gangBoosts = None
|
||||
|
||||
# If we're not explicitly asked to project fit onto something,
|
||||
# set self as target fit
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
# ===============================================================================
|
||||
# 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/>.
|
||||
# ===============================================================================
|
||||
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
|
||||
from eos.types import Skill, Module, Ship
|
||||
|
||||
|
||||
class Fleet(object):
|
||||
def calculateModifiedAttributes(self):
|
||||
# Make sure ALL fits in the gang have been calculated
|
||||
for c in chain(self.wings, (self.leader,)):
|
||||
if c is not None:
|
||||
c.calculateModifiedAttributes()
|
||||
|
||||
leader = self.leader
|
||||
self.booster = booster = self.booster if self.booster is not None else leader
|
||||
self.broken = False
|
||||
self.store = store = Store()
|
||||
store.set(booster, "fleet")
|
||||
# Go all the way down for each subtree we have.
|
||||
for wing in self.wings:
|
||||
wing.calculateGangBonusses(store)
|
||||
|
||||
# Check skill requirements and wing amount to see if we break or not
|
||||
if len(self.wings) == 0 or leader is None or leader.character is None or leader.character.getSkill(
|
||||
"Fleet Command").level < len(self.wings):
|
||||
self.broken = True
|
||||
|
||||
# Now calculate our own if we aren't broken
|
||||
if not self.broken:
|
||||
# We only get our own bonuses *Sadface*
|
||||
store.apply(leader, "fleet")
|
||||
|
||||
def recalculateLinear(self, withBoosters=True, dirtyStorage=None):
|
||||
self.store = Store()
|
||||
self.linearBoosts = {}
|
||||
if withBoosters is True:
|
||||
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
|
||||
"Fleet Command").level >= 1:
|
||||
self.leader.boostsFits.add(self.wings[0].squads[0].members[0].ID)
|
||||
self.leader.calculateModifiedAttributes()
|
||||
self.store.set(self.leader, "squad", clearingUpdate=True)
|
||||
else:
|
||||
self.store = Store()
|
||||
if self.leader is not None:
|
||||
try:
|
||||
self.leader.boostsFits.remove(self.wings[0].squads[0].members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
self.wings[0].recalculateLinear(self.store, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
|
||||
return self.linearBoosts
|
||||
|
||||
def count(self):
|
||||
total = 0
|
||||
for wing in self.wings:
|
||||
total += wing.count()
|
||||
|
||||
return total
|
||||
|
||||
def extend(self):
|
||||
self.wings.append(Wing())
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Fleet()
|
||||
copy.name = self.name
|
||||
copy.booster = deepcopy(self.booster)
|
||||
copy.leader = deepcopy(self.leader)
|
||||
for wing in self.wings:
|
||||
copy.wings.append(deepcopy(wing))
|
||||
|
||||
return copy
|
||||
|
||||
|
||||
class Wing(object):
|
||||
def calculateModifiedAttributes(self):
|
||||
for c in chain(self.squads, (self.leader,)):
|
||||
if c is not None:
|
||||
c.calculateModifiedAttributes()
|
||||
|
||||
def calculateGangBonusses(self, store):
|
||||
self.broken = False
|
||||
leader = self.leader
|
||||
self.booster = booster = self.booster if self.booster is not None else leader
|
||||
|
||||
store.set(booster, "wing")
|
||||
|
||||
# ALWAYS move down
|
||||
for squad in self.squads:
|
||||
squad.calculateGangBonusses(store)
|
||||
|
||||
# Check skill requirements and squad amount to see if we break or not
|
||||
if len(self.squads) == 0 or leader is None or leader.character is None or leader.character.getSkill(
|
||||
"Wing Command").level < len(self.squads):
|
||||
self.broken = True
|
||||
|
||||
# Check if we aren't broken, if we aren't, boost
|
||||
if not self.broken:
|
||||
store.apply(leader, "wing")
|
||||
else:
|
||||
# We broke, don't go up
|
||||
self.gang.broken = True
|
||||
|
||||
def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None):
|
||||
if withBoosters is True:
|
||||
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
|
||||
"Wing Command").level >= 1:
|
||||
self.leader.boostsFits.add(self.squads[0].members[0].ID)
|
||||
self.leader.calculateModifiedAttributes()
|
||||
store.set(self.leader, "squad", clearingUpdate=False)
|
||||
else:
|
||||
store = Store()
|
||||
if self.gang.leader is not None:
|
||||
try:
|
||||
self.gang.leader.boostsFits.remove(self.squads[0].members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
if self.leader is not None:
|
||||
try:
|
||||
self.leader.boostsFits.remove(self.squads[0].members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
self.squads[0].recalculateLinear(store, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
|
||||
|
||||
def count(self):
|
||||
total = 0 if self.leader is None else 1
|
||||
for squad in self.squads:
|
||||
total += squad.count()
|
||||
|
||||
return total
|
||||
|
||||
def extend(self):
|
||||
self.squads.append(Squad())
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Wing()
|
||||
copy.booster = deepcopy(self.booster)
|
||||
copy.leader = deepcopy(self.leader)
|
||||
for squad in self.squads:
|
||||
copy.squads.append(deepcopy(squad))
|
||||
|
||||
return copy
|
||||
|
||||
|
||||
class Squad(object):
|
||||
def calculateModifiedAttributes(self):
|
||||
for member in self.members:
|
||||
member.calculateModifiedAttributes()
|
||||
|
||||
def calculateGangBonusses(self, store):
|
||||
self.broken = False
|
||||
leader = self.leader
|
||||
self.booster = booster = self.booster if self.booster is not None else leader
|
||||
store.set(booster, "squad")
|
||||
|
||||
# Check skill requirements and squad size to see if we break or not
|
||||
if len(self.members) <= 0 or leader is None or leader.character is None or leader.character.getSkill(
|
||||
"Leadership").level * 2 < len(self.members):
|
||||
self.broken = True
|
||||
|
||||
if not self.broken:
|
||||
for member in self.members:
|
||||
store.apply(member, "squad")
|
||||
else:
|
||||
self.wing.broken = True
|
||||
|
||||
def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None):
|
||||
if withBoosters is True:
|
||||
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
|
||||
"Leadership").level >= 1:
|
||||
self.leader.boostsFits.add(self.members[0].ID)
|
||||
self.leader.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
|
||||
store.set(self.leader, "squad", clearingUpdate=False)
|
||||
else:
|
||||
store = Store()
|
||||
if self.leader is not None:
|
||||
try:
|
||||
self.leader.boostsFits.remove(self.members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
if self.wing.leader is not None:
|
||||
try:
|
||||
self.wing.leader.boostsFits.remove(self.members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
if self.wing.gang.leader is not None:
|
||||
try:
|
||||
self.wing.gang.leader.boostsFits.remove(self.members[0].ID)
|
||||
except KeyError:
|
||||
pass
|
||||
if getattr(self.wing.gang, "linearBoosts", None) is None:
|
||||
self.wing.gang.linearBoosts = {}
|
||||
dict = store.bonuses["squad"]
|
||||
for boostedAttr, boostInfoList in dict.iteritems():
|
||||
for boostInfo in boostInfoList:
|
||||
effect, thing = boostInfo
|
||||
# Get current boost value for given attribute, use 0 as fallback if
|
||||
# no boosts applied yet
|
||||
currBoostAmount = self.wing.gang.linearBoosts.get(boostedAttr, (0,))[0]
|
||||
# Attribute name which is used to get boost value
|
||||
newBoostAttr = effect.getattr("gangBonus") or "commandBonus"
|
||||
# Get boost amount for current boost
|
||||
newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0
|
||||
# Skill used to modify the gang bonus (for purposes of comparing old vs new)
|
||||
newBoostSkill = effect.getattr("gangBonusSkill")
|
||||
# If skill takes part in gang boosting, multiply by skill level
|
||||
if type(thing) == Skill:
|
||||
newBoostAmount *= thing.level
|
||||
# boost the gang bonus based on skill noted in effect file
|
||||
if newBoostSkill:
|
||||
newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level
|
||||
# If new boost is more powerful, replace older one with it
|
||||
if abs(newBoostAmount) > abs(currBoostAmount):
|
||||
self.wing.gang.linearBoosts[boostedAttr] = (newBoostAmount, boostInfo)
|
||||
|
||||
def count(self):
|
||||
return len(self.members)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Squad()
|
||||
copy.booster = deepcopy(self.booster)
|
||||
copy.leader = deepcopy(self.leader)
|
||||
for member in self.members:
|
||||
copy.members.append(deepcopy(member))
|
||||
|
||||
return copy
|
||||
|
||||
|
||||
class Store(object):
|
||||
def __init__(self):
|
||||
# Container for gang boosters and their respective bonuses, three-layered
|
||||
self.bonuses = {}
|
||||
for dictType in ("fleet", "wing", "squad"):
|
||||
self.bonuses[dictType] = {}
|
||||
# Container for boosted fits and corresponding boosts applied onto them
|
||||
self.boosts = {}
|
||||
|
||||
def set(self, fitBooster, layer, clearingUpdate=True):
|
||||
"""Add all gang boosts of given fit for given layer to boost store"""
|
||||
if fitBooster is None:
|
||||
return
|
||||
|
||||
# This dict contains all bonuses for specified layer
|
||||
dict = self.bonuses[layer]
|
||||
if clearingUpdate is True:
|
||||
# Clear existing bonuses
|
||||
dict.clear()
|
||||
|
||||
# Go through everything which can be used as gang booster
|
||||
for thing in chain(fitBooster.modules, fitBooster.implants, fitBooster.character.skills, (fitBooster.ship,)):
|
||||
if thing.item is None:
|
||||
continue
|
||||
for effect in thing.item.effects.itervalues():
|
||||
# And check if it actually has gang boosting effects
|
||||
if effect.isType("gang"):
|
||||
# Attribute which is boosted
|
||||
boostedAttr = effect.getattr("gangBoost")
|
||||
# List which contains all bonuses for given attribute for given layer
|
||||
l = dict.get(boostedAttr)
|
||||
# If there was no list, create it
|
||||
if l is None:
|
||||
l = dict[boostedAttr] = []
|
||||
# And append effect which is used to boost stuff and carrier of this effect
|
||||
l.append((effect, thing))
|
||||
|
||||
contextMap = {Skill: "skill",
|
||||
Ship: "ship",
|
||||
Module: "module"}
|
||||
|
||||
def apply(self, fitBoosted, layer):
|
||||
"""Applies all boosts onto given fit for given layer"""
|
||||
if fitBoosted is None:
|
||||
return
|
||||
# Boosts dict contains all bonuses applied onto given fit
|
||||
self.boosts[fitBoosted] = boosts = {}
|
||||
# Go through all bonuses for given layer, and find highest one per boosted attribute
|
||||
for currLayer in ("fleet", "wing", "squad"):
|
||||
# Dictionary with boosts for given layer
|
||||
dict = self.bonuses[currLayer]
|
||||
for boostedAttr, boostInfoList in dict.iteritems():
|
||||
for boostInfo in boostInfoList:
|
||||
effect, thing = boostInfo
|
||||
# Get current boost value for given attribute, use 0 as fallback if
|
||||
# no boosts applied yet
|
||||
currBoostAmount = boosts.get(boostedAttr, (0,))[0]
|
||||
# Attribute name which is used to get boost value
|
||||
newBoostAttr = effect.getattr("gangBonus") or "commandBonus"
|
||||
# Get boost amount for current boost
|
||||
newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0
|
||||
# Skill used to modify the gang bonus (for purposes of comparing old vs new)
|
||||
newBoostSkill = effect.getattr("gangBonusSkill")
|
||||
# If skill takes part in gang boosting, multiply by skill level
|
||||
if type(thing) == Skill:
|
||||
newBoostAmount *= thing.level
|
||||
# boost the gang bonus based on skill noted in effect file
|
||||
if newBoostSkill:
|
||||
newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level
|
||||
# If new boost is more powerful, replace older one with it
|
||||
if abs(newBoostAmount) > abs(currBoostAmount):
|
||||
boosts[boostedAttr] = (newBoostAmount, boostInfo)
|
||||
|
||||
# Don't look further down then current layer, wing commanders don't get squad bonuses and all that
|
||||
if layer == currLayer:
|
||||
break
|
||||
|
||||
self.modify(fitBoosted)
|
||||
|
||||
def getBoosts(self, fit):
|
||||
"""Return all boosts applied onto given fit"""
|
||||
return self.boosts.get(fit)
|
||||
|
||||
def modify(self, fitBoosted):
|
||||
# Get all boosts which should be applied onto current fit
|
||||
boosts = self.getBoosts(fitBoosted)
|
||||
# Now we got it all figured out, actually do the useful part of all this
|
||||
for name, info in boosts.iteritems():
|
||||
# Unpack all data required to run effect properly
|
||||
effect, thing = info[1]
|
||||
context = ("gang", self.contextMap[type(thing)])
|
||||
# Run effect, and get proper bonuses applied
|
||||
try:
|
||||
effect.handler(fitBoosted, thing, context)
|
||||
except:
|
||||
pass
|
||||
@@ -39,7 +39,6 @@ from eos.saveddata.implantSet import ImplantSet
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.fit import Fit, ImplantLocation
|
||||
from eos.saveddata.mode import Mode
|
||||
from eos.saveddata.fleet import Fleet, Wing, Squad
|
||||
from eos.saveddata.miscData import MiscData
|
||||
from eos.saveddata.override import Override
|
||||
|
||||
|
||||
Reference in New Issue
Block a user