Start implementing support for mutaplasmids.

* There's a new table `mutators` that has an FK back to a module, and links to an attribute ID with it's new value
* Fixes an issue in the item state where the "base" value showed the literal base value for the attribute, regardless of override / mutators
* If there is an override and a mutated attribute, mutated value takes effect
This commit is contained in:
blitzmann
2018-05-24 01:26:49 -04:00
parent 33d4a8a691
commit fabf759aa7
8 changed files with 132 additions and 9 deletions

View File

@@ -77,7 +77,7 @@ sd_lock = threading.RLock()
from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
# noinspection PyPep8 # noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \ from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
miscData, module, override, price, queries, skill, targetResists, user miscData, mutator, module, override, price, queries, skill, targetResists, user
# Import queries # Import queries
# noinspection PyPep8 # noinspection PyPep8

View File

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

View File

@@ -23,6 +23,7 @@ import datetime
from eos.db import saveddata_meta from eos.db import saveddata_meta
from eos.saveddata.module import Module from eos.saveddata.module import Module
from eos.saveddata.mutator import Mutator
from eos.saveddata.fit import Fit from eos.saveddata.fit import Fit
modules_table = Table("modules", saveddata_meta, modules_table = Table("modules", saveddata_meta,
@@ -39,4 +40,11 @@ modules_table = Table("modules", saveddata_meta,
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"')) CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
mapper(Module, modules_table, mapper(Module, modules_table,
properties={"owner": relation(Fit)}) properties={
"owner": relation(Fit),
"mutators": relation(
Mutator,
backref="module",
cascade="all,delete-orphan"
)
})

View File

@@ -0,0 +1,35 @@
# ===============================================================================
# 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, Boolean, DateTime, Float
from sqlalchemy.orm import mapper
import datetime
from eos.db import saveddata_meta
from eos.saveddata.mutator import Mutator
mutator_table = Table("mutators", saveddata_meta,
Column("moduleID", Integer, ForeignKey("modules.ID"), nullable=False, 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)

View File

@@ -33,6 +33,14 @@ class ItemAttrShortcut(object):
return return_value or default return return_value or default
def getBaseAttrValue(self, key, default=0):
'''
Gets base value in this order:
Mutated value > override value > attribute value
'''
return_value = self.itemModifiedAttributes.getOriginal(key)
return return_value or default
class ChargeAttrShortcut(object): class ChargeAttrShortcut(object):
def getModifiedChargeAttr(self, key, default=0): def getModifiedChargeAttr(self, key, default=0):
@@ -59,8 +67,10 @@ class ModifiedAttributeDict(collections.MutableMapping):
self.__modified = {} self.__modified = {}
# Affected by entities # Affected by entities
self.__affectedBy = {} self.__affectedBy = {}
# Overrides # Overrides (per item)
self.__overrides = {} self.__overrides = {}
# Mutators (per module)
self.__mutators = {}
# Dictionaries for various value modification types # Dictionaries for various value modification types
self.__forced = {} self.__forced = {}
self.__preAssigns = {} self.__preAssigns = {}
@@ -100,6 +110,14 @@ class ModifiedAttributeDict(collections.MutableMapping):
def overrides(self, val): def overrides(self, val):
self.__overrides = val self.__overrides = val
@property
def mutators(self):
return {x.attr.name: x for x in self.__mutators}
@mutators.setter
def mutators(self, val):
self.__mutators = val
def __getitem__(self, key): def __getitem__(self, key):
# Check if we have final calculated value # Check if we have final calculated value
key_value = self.__modified.get(key) key_value = self.__modified.get(key)
@@ -128,14 +146,16 @@ class ModifiedAttributeDict(collections.MutableMapping):
del self.__intermediary[key] del self.__intermediary[key]
def getOriginal(self, key, default=None): def getOriginal(self, key, default=None):
val = None
if self.overrides_enabled and self.overrides: if self.overrides_enabled and self.overrides:
val = self.overrides.get(key, None) val = self.overrides.get(key, val)
else:
val = None # mutators are overriden by overrides. x_x
val = self.mutators.get(key, val)
if val is None: if val is None:
if self.original: if self.original:
val = self.original.get(key, None) val = self.original.get(key, val)
if val is None and val != default: if val is None and val != default:
val = default val = default

View File

@@ -109,7 +109,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__charge = eos.db.getItem(self.chargeID) self.__charge = eos.db.getItem(self.chargeID)
self.build() self.build()
def build(self): def build(self):
""" Builds internal module variables from both init's """ """ Builds internal module variables from both init's """
@@ -129,6 +128,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.__item: if self.__item:
self.__itemModifiedAttributes.original = self.__item.attributes self.__itemModifiedAttributes.original = self.__item.attributes
self.__itemModifiedAttributes.mutators = self.mutators
self.__itemModifiedAttributes.overrides = self.__item.overrides self.__itemModifiedAttributes.overrides = self.__item.overrides
self.__hardpoint = self.__calculateHardpoint(self.__item) self.__hardpoint = self.__calculateHardpoint(self.__item)
self.__slot = self.__calculateSlot(self.__item) self.__slot = self.__calculateSlot(self.__item)

54
eos/saveddata/mutator.py Normal file
View File

@@ -0,0 +1,54 @@
# ===============================================================================
# Copyright (C) 2015 Ryan Holmes
#
# 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 logbook import Logger
from sqlalchemy.orm import reconstructor
import eos.db
from eos.eqBase import EqBase
pyfalog = Logger(__name__)
class Mutator(EqBase):
def __init__(self, stuff, attr, value):
self.moduleID = stuff.ID
self.attrID = attr.ID
self.__attr = attr
self.value = value
@reconstructor
def init(self):
self.__attr = None
self.__item = None
if self.attrID:
self.__attr = eos.db.getAttributeInfo(self.attrID)
if self.__attr is None:
pyfalog.error("Attribute (id: {0}) does not exist", self.attrID)
return
@property
def attr(self):
return self.__attr
@property
def item(self):
return self.__item

View File

@@ -174,7 +174,12 @@ class ItemParams(wx.Panel):
info = self.attrInfo.get(name) info = self.attrInfo.get(name)
att = self.attrValues[name] att = self.attrValues[name]
valDefault = getattr(info, "value", None) # If we're working with a stuff object, we should get the original value from our getBaseAttrValue function,
# which will return the value with respect to the effective base (with mutators / overrides in place)
valDefault = getattr(info, "value", None) # Get default value from attribute
if self.stuff is not None:
# if it's a stuff, overwrite default (with fallback to current value)
valDefault = self.stuff.getBaseAttrValue(name, valDefault)
valueDefault = valDefault if valDefault is not None else att valueDefault = valDefault if valDefault is not None else att
val = getattr(att, "value", None) val = getattr(att, "value", None)