From fabf759aa7e8f5e5a0db7a4c81b4e247bce5658b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 24 May 2018 01:26:49 -0400 Subject: [PATCH] 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 --- eos/db/__init__.py | 2 +- eos/db/saveddata/__init__.py | 1 + eos/db/saveddata/module.py | 10 +++- eos/db/saveddata/mutator.py | 35 +++++++++++++ eos/modifiedAttributeDict.py | 30 ++++++++++-- eos/saveddata/module.py | 2 +- eos/saveddata/mutator.py | 54 +++++++++++++++++++++ gui/builtinItemStatsViews/itemAttributes.py | 7 ++- 8 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 eos/db/saveddata/mutator.py create mode 100644 eos/saveddata/mutator.py diff --git a/eos/db/__init__.py b/eos/db/__init__.py index dd027841b..21b790421 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -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 # noinspection PyPep8 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 # noinspection PyPep8 diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index ba1ddad73..c36517623 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -1,6 +1,7 @@ __all__ = [ "character", "fit", + "mutator", "module", "user", "skill", diff --git a/eos/db/saveddata/module.py b/eos/db/saveddata/module.py index 149f4f73c..f84de348e 100644 --- a/eos/db/saveddata/module.py +++ b/eos/db/saveddata/module.py @@ -23,6 +23,7 @@ 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 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"')) mapper(Module, modules_table, - properties={"owner": relation(Fit)}) + properties={ + "owner": relation(Fit), + "mutators": relation( + Mutator, + backref="module", + cascade="all,delete-orphan" + ) + }) diff --git a/eos/db/saveddata/mutator.py b/eos/db/saveddata/mutator.py new file mode 100644 index 000000000..e503a5b25 --- /dev/null +++ b/eos/db/saveddata/mutator.py @@ -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 . +# =============================================================================== + +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) diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index b4c73c958..70968b1a9 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -33,6 +33,14 @@ class ItemAttrShortcut(object): 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): def getModifiedChargeAttr(self, key, default=0): @@ -59,8 +67,10 @@ class ModifiedAttributeDict(collections.MutableMapping): self.__modified = {} # Affected by entities self.__affectedBy = {} - # Overrides + # Overrides (per item) self.__overrides = {} + # Mutators (per module) + self.__mutators = {} # Dictionaries for various value modification types self.__forced = {} self.__preAssigns = {} @@ -100,6 +110,14 @@ class ModifiedAttributeDict(collections.MutableMapping): def overrides(self, 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): # Check if we have final calculated value key_value = self.__modified.get(key) @@ -128,14 +146,16 @@ class ModifiedAttributeDict(collections.MutableMapping): del self.__intermediary[key] def getOriginal(self, key, default=None): + val = None if self.overrides_enabled and self.overrides: - val = self.overrides.get(key, None) - else: - val = None + val = self.overrides.get(key, val) + + # mutators are overriden by overrides. x_x + val = self.mutators.get(key, val) if val is None: if self.original: - val = self.original.get(key, None) + val = self.original.get(key, val) if val is None and val != default: val = default diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index cf669f273..560caddf5 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -109,7 +109,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__charge = eos.db.getItem(self.chargeID) self.build() - def build(self): """ Builds internal module variables from both init's """ @@ -129,6 +128,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.__item: self.__itemModifiedAttributes.original = self.__item.attributes + self.__itemModifiedAttributes.mutators = self.mutators self.__itemModifiedAttributes.overrides = self.__item.overrides self.__hardpoint = self.__calculateHardpoint(self.__item) self.__slot = self.__calculateSlot(self.__item) diff --git a/eos/saveddata/mutator.py b/eos/saveddata/mutator.py new file mode 100644 index 000000000..eddc97570 --- /dev/null +++ b/eos/saveddata/mutator.py @@ -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 . +# =============================================================================== + +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 diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index e40710c45..5bd44e210 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -174,7 +174,12 @@ class ItemParams(wx.Panel): info = self.attrInfo.get(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 val = getattr(att, "value", None)