Merge branch 'EffectTest' of https://github.com/Ebag333/Pyfa into Ebag333-EffectTest

Conflicts:
	eos/saveddata/fit.py
	eos/saveddata/module.py
This commit is contained in:
Ryan Holmes
2016-11-22 12:34:10 -05:00
24 changed files with 192 additions and 57 deletions

View File

@@ -31,6 +31,7 @@ boosters_table = Table("boosters", saveddata_meta,
Column("active", Boolean), Column("active", Boolean),
) )
# Legacy booster side effect code, should disable but a mapper relies on it.
activeSideEffects_table = Table("boostersActiveSideEffects", saveddata_meta, activeSideEffects_table = Table("boostersActiveSideEffects", saveddata_meta,
Column("boosterID", ForeignKey("boosters.ID"), primary_key=True), Column("boosterID", ForeignKey("boosters.ID"), primary_key=True),
Column("effectID", Integer, primary_key=True)) Column("effectID", Integer, primary_key=True))

View File

@@ -3,6 +3,7 @@
# Used by: # Used by:
# Implants from group: Booster (12 of 47) # Implants from group: Booster (12 of 47)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -5,6 +5,7 @@
# Implants named like: Mindflood Booster (3 of 4) # Implants named like: Mindflood Booster (3 of 4)
# Implants named like: Sooth Sayer Booster (3 of 4) # Implants named like: Sooth Sayer Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -4,6 +4,7 @@
# Implants named like: Blue Pill Booster (3 of 5) # Implants named like: Blue Pill Booster (3 of 5)
# Implants named like: Exile Booster (3 of 4) # Implants named like: Exile Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -3,6 +3,7 @@
# Used by: # Used by:
# Implants from group: Booster (12 of 47) # Implants from group: Booster (12 of 47)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -4,6 +4,7 @@
# Implants named like: Exile Booster (3 of 4) # Implants named like: Exile Booster (3 of 4)
# Implants named like: Mindflood Booster (3 of 4) # Implants named like: Mindflood Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -3,6 +3,7 @@
# Used by: # Used by:
# Implants named like: Blue Pill Booster (3 of 5) # Implants named like: Blue Pill Booster (3 of 5)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -4,6 +4,7 @@
# Implants named like: Crash Booster (3 of 4) # Implants named like: Crash Booster (3 of 4)
# Implants named like: X Instinct Booster (3 of 4) # Implants named like: X Instinct Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -3,6 +3,7 @@
# Used by: # Used by:
# Implants from group: Booster (12 of 47) # Implants from group: Booster (12 of 47)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -4,6 +4,7 @@
# Implants named like: Drop Booster (3 of 4) # Implants named like: Drop Booster (3 of 4)
# Implants named like: X Instinct Booster (3 of 4) # Implants named like: X Instinct Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -5,6 +5,7 @@
# Implants named like: Mindflood Booster (3 of 4) # Implants named like: Mindflood Booster (3 of 4)
# Implants named like: Sooth Sayer Booster (3 of 4) # Implants named like: Sooth Sayer Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -4,6 +4,7 @@
# Implants named like: Exile Booster (3 of 4) # Implants named like: Exile Booster (3 of 4)
# Implants named like: Frentix Booster (3 of 4) # Implants named like: Frentix Booster (3 of 4)
type = "boosterSideEffect" type = "boosterSideEffect"
activeByDefault = False
def handler(fit, booster, context): def handler(fit, booster, context):

View File

@@ -83,6 +83,40 @@ class Effect(EqBase):
return self.__runTime return self.__runTime
@property
def activeByDefault(self):
"""
The state that this effect should be be in.
This property is also automaticly fetched from effects/<effectName>.py if the file exists.
the possible values are:
None, True, False
If this is not set:
We simply assume that missing/none = True, and set it accordingly
(much as we set runTime to Normalif not otherwise set).
Nearly all effect files will fall under this category.
If this is set to True:
We would enable it anyway, but hey, it's double enabled.
No effect files are currently configured this way (and probably will never be).
If this is set to False:
Basically we simply skip adding the effect to the effect handler when the effect is called,
much as if the run time didn't match or other criteria failed.
"""
if not self.__generated:
self.__generateHandler()
return self.__activeByDefault
@activeByDefault.setter
def activeByDefault(self, value):
"""
Just assign the input values to the activeByDefault attribute.
You *could* do something more interesting here if you wanted.
"""
self.__activeByDefault = value
@property @property
def type(self): def type(self):
""" """
@@ -133,6 +167,11 @@ class Effect(EqBase):
except AttributeError: except AttributeError:
self.__runTime = "normal" self.__runTime = "normal"
try:
self.__activeByDefault = getattr(effectModule, "activeByDefault")
except AttributeError:
self.__activeByDefault = True
try: try:
t = getattr(effectModule, "type") t = getattr(effectModule, "type")
except AttributeError: except AttributeError:
@@ -143,6 +182,7 @@ class Effect(EqBase):
except (ImportError, AttributeError) as e: except (ImportError, AttributeError) as e:
self.__handler = effectDummy self.__handler = effectDummy
self.__runTime = "normal" self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None self.__type = None
except Exception as e: except Exception as e:
traceback.print_exc(e) traceback.print_exc(e)

View File

@@ -64,13 +64,18 @@ class Booster(HandledItem, ItemAttrShortcut):
self.__itemModifiedAttributes.overrides = self.__item.overrides self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__slot = self.__calculateSlot(self.__item) self.__slot = self.__calculateSlot(self.__item)
# Legacy booster side effect code, disabling as not currently implemented
'''
for effect in self.__item.effects.itervalues(): for effect in self.__item.effects.itervalues():
if effect.isType("boosterSideEffect"): if effect.isType("boosterSideEffect"):
s = SideEffect(self) s = SideEffect(self)
s.effect = effect s.effect = effect
s.active = effect.ID in self.__activeSideEffectIDs s.active = effect.ID in self.__activeSideEffectIDs
self.__sideEffects.append(s) self.__sideEffects.append(s)
'''
# Legacy booster side effect code, disabling as not currently implemented
'''
def iterSideEffects(self): def iterSideEffects(self):
return self.__sideEffects.__iter__() return self.__sideEffects.__iter__()
@@ -80,6 +85,7 @@ class Booster(HandledItem, ItemAttrShortcut):
return sideEffect return sideEffect
raise KeyError("SideEffect with %s as name not found" % name) raise KeyError("SideEffect with %s as name not found" % name)
'''
@property @property
def itemModifiedAttributes(self): def itemModifiedAttributes(self):
@@ -112,12 +118,17 @@ class Booster(HandledItem, ItemAttrShortcut):
if not self.active: if not self.active:
return return
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime and effect.isType("passive"): if effect.runTime == runTime and \
(effect.isType("passive") or effect.isType("boosterSideEffect")) and \
effect.activeByDefault:
effect.handler(fit, self, ("booster",)) effect.handler(fit, self, ("booster",))
# Legacy booster code, not fully implemented
'''
for sideEffect in self.iterSideEffects(): for sideEffect in self.iterSideEffects():
if sideEffect.active and sideEffect.effect.runTime == runTime: if sideEffect.active and sideEffect.effect.runTime == runTime:
sideEffect.effect.handler(fit, self, ("boosterSideEffect",)) sideEffect.effect.handler(fit, self, ("boosterSideEffect",))
'''
@validates("ID", "itemID", "ammoID", "active") @validates("ID", "itemID", "ammoID", "active")
def validator(self, key, val): def validator(self, key, val):
@@ -135,46 +146,52 @@ class Booster(HandledItem, ItemAttrShortcut):
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
copy = Booster(self.item) copy = Booster(self.item)
copy.active = self.active copy.active = self.active
# Legacy booster side effect code, disabling as not currently implemented
'''
origSideEffects = list(self.iterSideEffects()) origSideEffects = list(self.iterSideEffects())
copySideEffects = list(copy.iterSideEffects()) copySideEffects = list(copy.iterSideEffects())
i = 0 i = 0
while i < len(origSideEffects): while i < len(origSideEffects):
copySideEffects[i].active = origSideEffects[i].active copySideEffects[i].active = origSideEffects[i].active
i += 1 i += 1
'''
return copy return copy
# Legacy booster side effect code, disabling as not currently implemented
'''
class SideEffect(object):
def __init__(self, owner):
self.__owner = owner
self.__active = False
self.__effect = None
class SideEffect(object): @property
def __init__(self, owner): def active(self):
self.__owner = owner return self.__active
self.__active = False
self.__effect = None
@property @active.setter
def active(self): def active(self, active):
return self.__active if not isinstance(active, bool):
raise TypeError("Expecting a bool, not a " + type(active))
@active.setter if active != self.__active:
def active(self, active): if active:
if not isinstance(active, bool): self.__owner._Booster__activeSideEffectIDs.append(self.effect.ID)
raise TypeError("Expecting a bool, not a " + type(active)) else:
self.__owner._Booster__activeSideEffectIDs.remove(self.effect.ID)
if active != self.__active: self.__active = active
if active:
self.__owner._Booster__activeSideEffectIDs.append(self.effect.ID)
else:
self.__owner._Booster__activeSideEffectIDs.remove(self.effect.ID)
self.__active = active @property
def effect(self):
return self.__effect
@property @effect.setter
def effect(self): def effect(self, effect):
return self.__effect if not hasattr(effect, "handler"):
raise TypeError("Need an effect with a handler")
@effect.setter self.__effect = effect
def effect(self, effect): '''
if not hasattr(effect, "handler"):
raise TypeError("Need an effect with a handler")
self.__effect = effect

View File

@@ -335,8 +335,10 @@ class Skill(HandledItem):
return return
for effect in item.effects.itervalues(): for effect in item.effects.itervalues():
if effect.runTime == runTime and effect.isType("passive") and \ if effect.runTime == runTime and \
(not fit.isStructure or effect.isType("structure")): effect.isType("passive") and \
(not fit.isStructure or effect.isType("structure")) and \
effect.activeByDefault:
try: try:
effect.handler(fit, self, ("skill",)) effect.handler(fit, self, ("skill",))
except AttributeError: except AttributeError:

View File

@@ -232,6 +232,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime and \ if effect.runTime == runTime and \
effect.activeByDefault and \
((projected is True and effect.isType("projected")) or ((projected is True and effect.isType("projected")) or
projected is False and effect.isType("passive")): projected is False and effect.isType("passive")):
# See GH issue #765 # See GH issue #765
@@ -245,7 +246,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.charge: if self.charge:
for effect in self.charge.effects.itervalues(): for effect in self.charge.effects.itervalues():
if effect.runTime == runTime: if effect.runTime == runTime and effect.activeByDefault:
effect.handler(fit, self, ("droneCharge",)) effect.handler(fit, self, ("droneCharge",))
def __deepcopy__(self, memo): def __deepcopy__(self, memo):

View File

@@ -270,6 +270,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if ability.active: if ability.active:
effect = ability.effect effect = ability.effect
if effect.runTime == runTime and \ if effect.runTime == runTime and \
effect.activeByDefault and \
((projected and effect.isType("projected")) or not projected): ((projected and effect.isType("projected")) or not projected):
if ability.grouped: if ability.grouped:
effect.handler(fit, self, context) effect.handler(fit, self, context)

View File

@@ -462,6 +462,8 @@ class Fit(object):
context = ("commandRun", thing.__class__.__name__.lower()) context = ("commandRun", thing.__class__.__name__.lower())
if isinstance(thing, Module): if isinstance(thing, Module):
# This should always be a gang effect, otherwise it wouldn't be added to commandBonuses
# @todo: Check this
if effect.isType("gang"): if effect.isType("gang"):
# todo: ensure that these are run with the module is active only # todo: ensure that these are run with the module is active only
context += ("commandRun",) context += ("commandRun",)

View File

@@ -93,7 +93,7 @@ class Implant(HandledItem, ItemAttrShortcut):
if not self.active: if not self.active:
return return
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime and effect.isType("passive"): if effect.runTime == runTime and effect.isType("passive") and effect.activeByDefault:
effect.handler(fit, self, ("implant",)) effect.handler(fit, self, ("implant",))
@validates("fitID", "itemID", "active") @validates("fitID", "itemID", "active")

View File

@@ -51,5 +51,5 @@ class Mode(ItemAttrShortcut, HandledItem):
def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): def calculateModifiedAttributes(self, fit, runTime, forceProjected=False):
if self.item: if self.item:
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime: if effect.runTime == runTime and effect.activeByDefault:
effect.handler(fit, self, context=("module",)) effect.handler(fit, self, context=("module",))

View File

@@ -625,6 +625,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if not projected or (self.projected and not forceProjected) or gang: if not projected or (self.projected and not forceProjected) or gang:
for effect in self.charge.effects.itervalues(): for effect in self.charge.effects.itervalues():
if effect.runTime == runTime and \ if effect.runTime == runTime and \
effect.activeByDefault and \
(effect.isType("offline") or (effect.isType("offline") or
(effect.isType("passive") and self.state >= State.ONLINE) or (effect.isType("passive") and self.state >= State.ONLINE) or
(effect.isType("active") and self.state >= State.ACTIVE)) and \ (effect.isType("active") and self.state >= State.ACTIVE)) and \
@@ -645,19 +646,18 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if effect.runTime == runTime and \ if effect.runTime == runTime and \
effect.isType("overheat") \ effect.isType("overheat") \
and not forceProjected \ and not forceProjected \
and effect.activeByDefault \
and ((gang and effect.isType("gang")) or not gang): and ((gang and effect.isType("gang")) or not gang):
effect.handler(fit, self, context) effect.handler(fit, self, context)
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime and \ if effect.runTime == runTime and \
effect.activeByDefault and \
(effect.isType("offline") or (effect.isType("offline") or
(effect.isType("passive") and self.state >= State.ONLINE) or (effect.isType("passive") and self.state >= State.ONLINE) or
(effect.isType("active") and self.state >= State.ACTIVE))\ (effect.isType("active") and self.state >= State.ACTIVE))\
and ((projected and effect.isType("projected")) or not projected)\ and ((projected and effect.isType("projected")) or not projected)\
and ((gang and effect.isType("gang")) or not gang): and ((gang and effect.isType("gang")) or not gang):
thing = effect.isType("gang")
if gang:
pass
effect.handler(fit, self, context) effect.handler(fit, self, context)
@property @property

View File

@@ -82,7 +82,9 @@ class Ship(ItemAttrShortcut, HandledItem):
if forceProjected: if forceProjected:
return return
for effect in self.item.effects.itervalues(): for effect in self.item.effects.itervalues():
if effect.runTime == runTime and effect.isType("passive"): if effect.runTime == runTime and \
effect.isType("passive") and \
effect.activeByDefault:
# Ships have effects that utilize the level of a skill as an # Ships have effects that utilize the level of a skill as an
# additional operator to the modifier. These are defined in # additional operator to the modifier. These are defined in
# the effect itself, and these skillbooks are registered when # the effect itself, and these skillbooks are registered when

View File

@@ -34,7 +34,8 @@ from eos.saveddata.fighter import Fighter
from eos.saveddata.cargo import Cargo from eos.saveddata.cargo import Cargo
from eos.saveddata.implant import Implant from eos.saveddata.implant import Implant
from eos.saveddata.implantSet import ImplantSet from eos.saveddata.implantSet import ImplantSet
from eos.saveddata.booster import SideEffect # Legacy booster side effect code, disabling as not currently implemented
# from eos.saveddata.booster import SideEffect
from eos.saveddata.booster import Booster from eos.saveddata.booster import Booster
from eos.saveddata.fit import Fit, ImplantLocation from eos.saveddata.fit import Fit, ImplantLocation
from eos.saveddata.mode import Mode from eos.saveddata.mode import Mode

View File

@@ -740,31 +740,44 @@ class ItemRequirements ( wx.Panel ):
class ItemEffects (wx.Panel): class ItemEffects (wx.Panel):
def __init__(self, parent, stuff, item): def __init__(self, parent, stuff, item):
wx.Panel.__init__ (self, parent) wx.Panel.__init__(self, parent)
mainSizer = wx.BoxSizer( wx.VERTICAL ) self.item = item
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.effectList = AutoListCtrl(self, wx.ID_ANY, self.effectList = AutoListCtrl(self, wx.ID_ANY,
style = style=
#wx.LC_HRULES | # wx.LC_HRULES |
#wx.LC_NO_HEADER | # wx.LC_NO_HEADER |
wx.LC_REPORT |wx.LC_SINGLE_SEL |wx.LC_VRULES |wx.NO_BORDER) wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
mainSizer.Add( self.effectList, 1, wx.ALL|wx.EXPAND, 0 ) mainSizer.Add(self.effectList, 1, wx.ALL | wx.EXPAND, 0)
self.SetSizer( mainSizer ) self.SetSizer(mainSizer)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick, self.effectList)
if config.debug: if config.debug:
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick, self.effectList) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick, self.effectList)
self.PopulateList()
def PopulateList(self):
self.effectList.InsertColumn(0,"Name") self.effectList.InsertColumn(0,"Name")
self.effectList.InsertColumn(1,"Implemented") self.effectList.InsertColumn(1,"Active")
self.effectList.InsertColumn(2, "Type")
if config.debug: if config.debug:
self.effectList.InsertColumn(2,"ID") self.effectList.InsertColumn(3, "Run Time")
self.effectList.InsertColumn(4,"ID")
#self.effectList.SetColumnWidth(0,385) #self.effectList.SetColumnWidth(0,385)
self.effectList.setResizeColumn(0) self.effectList.setResizeColumn(0)
self.effectList.SetColumnWidth(1,50)
self.effectList.SetColumnWidth(2, 80)
if config.debug:
self.effectList.SetColumnWidth(3, 65)
self.effectList.SetColumnWidth(4, 40)
self.effectList.SetColumnWidth(1,100) item = self.item
effects = item.effects effects = item.effects
names = list(effects.iterkeys()) names = list(effects.iterkeys())
names.sort() names.sort()
@@ -772,19 +785,55 @@ class ItemEffects (wx.Panel):
for name in names: for name in names:
index = self.effectList.InsertStringItem(sys.maxint, name) index = self.effectList.InsertStringItem(sys.maxint, name)
try: if effects[name].isImplemented:
implemented = "Yes" if effects[name].isImplemented else "No" if effects[name].activeByDefault:
except: activeByDefault = "Yes"
implemented = "Erroneous" else:
activeByDefault = "No"
else:
activeByDefault = ""
self.effectList.SetStringItem(index, 1, implemented) effectTypeText = ""
if effects[name].type:
for effectType in effects[name].type:
effectTypeText += effectType + " "
pass
if effects[name].runTime and effects[name].isImplemented:
effectRunTime = str(effects[name].runTime)
else:
effectRunTime = ""
self.effectList.SetStringItem(index, 1, activeByDefault)
self.effectList.SetStringItem(index, 2, effectTypeText)
if config.debug: if config.debug:
self.effectList.SetStringItem(index, 2, str(effects[name].ID)) self.effectList.SetStringItem(index, 3, effectRunTime)
self.effectList.SetStringItem(index, 4, str(effects[name].ID))
self.effectList.RefreshRows() self.effectList.RefreshRows()
self.Layout() self.Layout()
def OnClick(self, event): def OnClick(self, event):
"""
Debug use: toggle effects on/off.
Affects *ALL* items that use that effect.
Is not stateful. Will reset if Pyfa is closed and reopened.
"""
try:
activeByDefault = getattr(self.item.effects[event.GetText()], "activeByDefault")
if activeByDefault:
setattr(self.item.effects[event.GetText()], "activeByDefault", False)
else:
setattr(self.item.effects[event.GetText()], "activeByDefault", True)
except AttributeError:
# Attribute doesn't exist, do nothing
pass
self.RefreshValues(event)
def OnRightClick(self, event):
""" """
Debug use: open effect file with default application. Debug use: open effect file with default application.
If effect file does not exist, create it If effect file does not exist, create it
@@ -804,6 +853,14 @@ class ItemEffects (wx.Panel):
import subprocess import subprocess
subprocess.call(["xdg-open", file]) subprocess.call(["xdg-open", file])
def RefreshValues(self, event):
self.Freeze()
self.effectList.ClearAll()
self.PopulateList()
self.effectList.RefreshRows()
self.Layout()
self.Thaw()
event.Skip()
########################################################################### ###########################################################################
## Class ItemAffectedBy ## Class ItemAffectedBy