Browse Source

The return of eft(pyfa?) cog. Forgive me father, for I have sinned.

pull/4/head
Mike 4 years ago
parent
commit
1c445e2393
  1. 2
      .gitignore
  2. 1
      bot.py
  3. 727
      cogs/eft.py
  4. 2
      eos/__init__.py
  5. 210
      eos/capSim.py
  6. 32
      eos/config.py
  7. 103
      eos/const.py
  8. 101
      eos/db/__init__.py
  9. 2
      eos/db/gamedata/__init__.py
  10. 50
      eos/db/gamedata/alphaClones.py
  11. 65
      eos/db/gamedata/attribute.py
  12. 38
      eos/db/gamedata/category.py
  13. 65
      eos/db/gamedata/dynamicAttributes.py
  14. 46
      eos/db/gamedata/effect.py
  15. 40
      eos/db/gamedata/group.py
  16. 70
      eos/db/gamedata/item.py
  17. 43
      eos/db/gamedata/marketGroup.py
  18. 30
      eos/db/gamedata/metaData.py
  19. 51
      eos/db/gamedata/metaGroup.py
  20. 442
      eos/db/gamedata/queries.py
  21. 11
      eos/db/gamedata/traits.py
  22. 35
      eos/db/gamedata/unit.py
  23. 44
      eos/db/migration.py
  24. 50
      eos/db/migrations/__init__.py
  25. 99
      eos/db/migrations/upgrade1.py
  26. 17
      eos/db/migrations/upgrade10.py
  27. 116
      eos/db/migrations/upgrade11.py
  28. 340
      eos/db/migrations/upgrade12.py
  29. 16
      eos/db/migrations/upgrade13.py
  30. 21
      eos/db/migrations/upgrade14.py
  31. 19
      eos/db/migrations/upgrade15.py
  32. 15
      eos/db/migrations/upgrade16.py
  33. 42
      eos/db/migrations/upgrade17.py
  34. 68
      eos/db/migrations/upgrade18.py
  35. 18
      eos/db/migrations/upgrade19.py
  36. 22
      eos/db/migrations/upgrade2.py
  37. 15
      eos/db/migrations/upgrade20.py
  38. 10
      eos/db/migrations/upgrade21.py
  39. 45
      eos/db/migrations/upgrade22.py
  40. 13
      eos/db/migrations/upgrade23.py
  41. 14
      eos/db/migrations/upgrade24.py
  42. 4246
      eos/db/migrations/upgrade25.py
  43. 9
      eos/db/migrations/upgrade26.py
  44. 9
      eos/db/migrations/upgrade27.py
  45. 18
      eos/db/migrations/upgrade28.py
  46. 18
      eos/db/migrations/upgrade29.py
  47. 14
      eos/db/migrations/upgrade3.py
  48. 17
      eos/db/migrations/upgrade30.py
  49. 15
      eos/db/migrations/upgrade31.py
  50. 141
      eos/db/migrations/upgrade4.py
  51. 9
      eos/db/migrations/upgrade5.py
  52. 11
      eos/db/migrations/upgrade6.py
  53. 23
      eos/db/migrations/upgrade7.py
  54. 85
      eos/db/migrations/upgrade8.py
  55. 25
      eos/db/migrations/upgrade9.py
  56. 18
      eos/db/saveddata/__init__.py
  57. 55
      eos/db/saveddata/booster.py
  58. 41
      eos/db/saveddata/cargo.py
  59. 91
      eos/db/saveddata/character.py
  60. 39
      eos/db/saveddata/damagePattern.py
  61. 239
      eos/db/saveddata/databaseRepair.py
  62. 43
      eos/db/saveddata/drone.py
  63. 55
      eos/db/saveddata/fighter.py
  64. 259
      eos/db/saveddata/fit.py
  65. 47
      eos/db/saveddata/implant.py
  66. 49
      eos/db/saveddata/implantSet.py
  67. 207
      eos/db/saveddata/loadDefaultDatabaseValues.py
  68. 30
      eos/db/saveddata/miscData.py
  69. 56
      eos/db/saveddata/module.py
  70. 35
      eos/db/saveddata/mutator.py
  71. 35
      eos/db/saveddata/override.py
  72. 35
      eos/db/saveddata/price.py
  73. 565
      eos/db/saveddata/queries.py
  74. 36
      eos/db/saveddata/skill.py
  75. 39
      eos/db/saveddata/targetResists.py
  76. 32
      eos/db/saveddata/user.py
  77. 73
      eos/db/util.py
  78. 428
      eos/effectHandlerHelpers.py
  79. 35812
      eos/effects.py
  80. 31
      eos/eqBase.py
  81. BIN
      eos/eve.db
  82. 86
      eos/events.py
  83. 741
      eos/gamedata.py
  84. 76
      eos/graph/__init__.py
  85. 15
      eos/graph/fitCapAmountVsTime.py
  86. 14
      eos/graph/fitCapRegenVsCapPerc.py
  87. 17
      eos/graph/fitDistanceVsTime.py
  88. 151
      eos/graph/fitDmgVsTime.py
  89. 194
      eos/graph/fitDpsVsRange.py
  90. 165
      eos/graph/fitDpsVsTime.py
  91. 27
      eos/graph/fitShieldAmountVsTime.py
  92. 22
      eos/graph/fitShieldRegenVsShieldPerc.py
  93. 14
      eos/graph/fitSpeedVsTime.py
  94. 98
      eos/graph/fitWarpTimeVsDistance.py
  95. 503
      eos/lgpl.txt
  96. 447
      eos/modifiedAttributeDict.py
  97. BIN
      eos/saveddata.db
  98. 0
      eos/saveddata/__init__.py
  99. 164
      eos/saveddata/booster.py
  100. 65
      eos/saveddata/boosterSideEffect.py

2
.gitignore

@ -1,6 +1,6 @@
.idea/
*.pyc
config.py
/config.py
Rooster.log
__pycache__/
cogs/__pycache__/

1
bot.py

@ -39,6 +39,7 @@ initial_cogs = (
"cogs.joinchannel",
"cogs.route",
"cogs.memberjoin",
"cogs.eft",
)

727
cogs/eft.py

@ -0,0 +1,727 @@
# THIS COG WOULD NOT BE POSSIBLE WITHOUT ME STEALING HALF OF PYFAS CODEBASE
# SERIOUSLY THEY DID THE HARD WORK I JUST DISCORDED IT HAVE MERCY
# https://github.com/pyfa-org/Pyfa
import logging
import aiohttp
import re
from enum import IntEnum
import eos
import eos.db
from eos.gamedata import *
from eos.db.gamedata.queries import getDynamicItem
from eos.saveddata.booster import Booster
from eos.saveddata.implant import Implant
from eos.saveddata.cargo import Cargo
from eos.saveddata.fighter import Fighter
from eos.saveddata.drone import Drone
from eos.saveddata.ship import Ship
from eos.saveddata.citadel import Citadel
from eos.saveddata.module import Module
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.fit import Fit
from eos.const import FittingModuleState
from eos.const import FittingSlot
import eos.db.gamedata.queries as queries
from eos.db.gamedata.queries import getAttributeInfo
import eos.db.saveddata.queries as s_queries
from discord.ext import commands
from discord.embeds import Embed
log = logging.getLogger(__name__)
class PortEftOptions(IntEnum):
"""
Contains different options for eft-export
"""
IMPLANTS = 1
MUTATIONS = 2
LOADED_CHARGES = 3
EFT_OPTIONS = (
(
PortEftOptions.LOADED_CHARGES,
"Loaded Charges",
"Export charges loaded into modules",
True,
),
(
PortEftOptions.MUTATIONS,
"Mutated Attributes",
"Export mutated modules' stats",
True,
),
(
PortEftOptions.IMPLANTS,
"Implants && Boosters",
"Export implants and boosters",
True,
),
)
MODULE_CATS = ("Module", "Subsystem", "Structure Module")
SLOT_ORDER = (
FittingSlot.LOW,
FittingSlot.MED,
FittingSlot.HIGH,
FittingSlot.RIG,
FittingSlot.SUBSYSTEM,
FittingSlot.SERVICE,
)
OFFLINE_SUFFIX = "/OFFLINE"
class EFT(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.eftregex = re.compile("\[.+?, .+]")
@commands.Cog.listener()
async def on_message(self, message):
# force fits to be shown in code blocks, its just good form :sun:
if not message.content.startswith("```"):
return
else:
potentialfit = message.content.strip("```").strip()
fit = getfit(potentialfit)
if not fit:
print("no fit found")
return
await message.channel.trigger_typing()
# do some calculatin on the ship with an all v toonie
fit.calculateModifiedAttributes()
fit.calculateDroneDmgStats()
fit.calculateWeaponDmgStats(spoolOptions=None)
# omni damage pattern needed for tank stats like res and ehp
fit.damagePattern = DamagePattern()
# price check
# im not a big fan of making a new session here, but i dont see an easy way to get ahold of our normal method
async with aiohttp.ClientSession() as session:
async with session.post(
"https://evepraisal.com/appraisal",
data={"market": "jita", "raw_textarea": potentialfit},
) as resp:
if resp.status != 200:
price = -1
else:
app_id = resp.headers["X-Appraisal-Id"]
app_url = f"https://evepraisal.com/a/{app_id}.json"
async with session.get(app_url) as resp:
if resp.status != 200:
price = -1
else:
price = await resp.json()
price = price["totals"]["sell"]
embed = Embed(title=f"{fit.ship.name} - {fit.name}")
embed.set_thumbnail(
url=f"https://image.eveonline.com/Render/{fit.shipID}_128.png"
)
embed.add_field(name="Raw HP", value=f"{round(sum(fit.hp.values())):,}")
embed.add_field(name="EHP (omni)", value=f"{round(sum(fit.ehp.values())):,}")
embed.add_field(
name="DPS(estimate)/Volley",
value=f"{fit.getTotalDps().total:.2f}/{fit.getTotalVolley().total:,.2f}",
)
if fit.drones:
embed.set_footer(
text=f"*Dps calcs are using {fit.activeDrones} {queries.getItem(fit.drones[-1].item.ID).name} drones"
)
if fit.capStable:
embed.add_field(name="Capacitor", value="Stable!")
else:
embed.add_field(name="Cap length", value=f"{fit.capState:.2f} seconds")
embed.add_field(name="Align", value=f"{fit.alignTime:.2f} Seconds")
embed.add_field(name="Total Price", value=f"{price:,.2f} isk")
# active reps, also includes normal shield regen, so maybe check for that before using this
# embed.add_field(name="Active Tank", value=f"{sum(fit.effectiveSustainableTank.values()):.2f} EHP/s")
# i really really want to include the resistance profiles, but its a lot of info and will be spammy
# for tankType in ("shield", "armor", "hull"):
# for damageType in ("em", "thermal", "kinetic", "explosive"):
# if fit is not None:
# resonanceType = tankType if tankType != "hull" else ""
# resonance = "%s%sDamageResonance" % (resonanceType, damageType.capitalize())
# resonance = resonance[0].lower() + resonance[1:]
# resonance = (1 - fit.ship.getModifiedItemAttr(resonance)) * 100
# else:
# resonance = 0
# embed.add_field(name=tankType, value=f"{damageType.capitalize()} {resonance.__round__(2)}")
return await message.channel.send(embed=embed)
###
# most everything past this point has been ripped straight from pyfa/eos with minor modification
# i am but a script kiddie and not very good at this
# https://github.com/pyfa-org/Pyfa
def getfit(potentialfit):
lines = _importPrepare(potentialfit)
try:
fit = _importCreateFit(lines)
except EftImportError as e:
return
fit.character = s_queries.Character.getAll5()
aFit = AbstractFit()
aFit.mutations = _importGetMutationData(lines)
nameChars = "[^,/\[\]]" # Characters which are allowed to be used in name
stubPattern = "^\[.+?\]$"
modulePattern = "^(?P<typeName>{0}+?)(,\s*(?P<chargeName>{0}+?))?(?P<offline>\s*{1})?(\s*\[(?P<mutation>\d+?)\])?$".format(
nameChars, OFFLINE_SUFFIX
)
droneCargoPattern = "^(?P<typeName>{}+?) x(?P<amount>\d+?)$".format(nameChars)
sections = []
for section in _importSectionIter(lines):
for line in section.lines:
# Stub line
if re.match(stubPattern, line):
section.itemSpecs.append(None)
continue
# Items with quantity specifier
m = re.match(droneCargoPattern, line)
if m:
try:
itemSpec = MultiItemSpec(m.group("typeName"))
# Items which cannot be fetched are considered as stubs
except EftImportError:
section.itemSpecs.append(None)
else:
itemSpec.amount = int(m.group("amount"))
section.itemSpecs.append(itemSpec)
continue
# All other items
m = re.match(modulePattern, line)
if m:
try:
itemSpec = RegularItemSpec(
m.group("typeName"), chargeName=m.group("chargeName")
)
# Items which cannot be fetched are considered as stubs
except EftImportError:
section.itemSpecs.append(None)
else:
if m.group("offline"):
itemSpec.offline = True
if m.group("mutation"):
itemSpec.mutationIdx = int(m.group("mutation"))
section.itemSpecs.append(itemSpec)
continue
_clearTail(section.itemSpecs)
sections.append(section)
hasDroneBay = any(s.isDroneBay for s in sections)
hasFighterBay = any(s.isFighterBay for s in sections)
for section in sections:
if section.isModuleRack:
aFit.addModules(section.itemSpecs)
elif section.isImplantRack:
for itemSpec in section.itemSpecs:
aFit.addImplant(itemSpec)
elif section.isDroneBay:
for itemSpec in section.itemSpecs:
aFit.addDrone(itemSpec)
elif section.isFighterBay:
for itemSpec in section.itemSpecs:
aFit.addFighter(itemSpec)
elif section.isCargoHold:
for itemSpec in section.itemSpecs:
aFit.addCargo(itemSpec)
# Mix between different kinds of item specs (can happen when some
# blank lines are removed)
else:
for itemSpec in section.itemSpecs:
if itemSpec is None:
continue
if itemSpec.isModule:
aFit.addModule(itemSpec)
elif itemSpec.isImplant:
aFit.addImplant(itemSpec)
elif itemSpec.isDrone and not hasDroneBay:
aFit.addDrone(itemSpec)
elif itemSpec.isFighter and not hasFighterBay:
aFit.addFighter(itemSpec)
elif itemSpec.isCargo:
aFit.addCargo(itemSpec)
# Subsystems first because they modify slot amount
for i, m in enumerate(aFit.subsystems):
if m is None:
dummy = Module.buildEmpty(aFit.getSlotByContainer(aFit.subsystems))
dummy.owner = fit
fit.modules.replaceRackPosition(i, dummy)
elif m.fits(fit):
m.owner = fit
fit.modules.replaceRackPosition(i, m)
# Other stuff
for modRack in (
aFit.rigs,
aFit.services,
aFit.modulesHigh,
aFit.modulesMed,
aFit.modulesLow,
):
for i, m in enumerate(modRack):
if m is None:
dummy = Module.buildEmpty(aFit.getSlotByContainer(modRack))
dummy.owner = fit
fit.modules.replaceRackPosition(i, dummy)
elif m.fits(fit):
m.owner = fit
if not m.isValidState(m.state):
logging.warning(
"service.port.eft.importEft: module {} cannot have state {}",
m,
m.state,
)
fit.modules.replaceRackPosition(i, m)
for implant in aFit.implants:
fit.implants.append(implant)
for booster in aFit.boosters:
fit.boosters.append(booster)
for drone in aFit.drones.values():
fit.drones.append(drone)
for fighter in aFit.fighters:
fit.fighters.append(fighter)
for cargo in aFit.cargo.values():
fit.cargo.append(cargo)
# ugly ugly ugly hack to try to activate the last block of drones we have in the drone bay
# probably can be improved a lot by filtering out ewar and searching for highest deeps
try:
print(drone.item.getShortName())
has = drone.amount
canuse = fit.getReleaseLimitForDrone(drone.item)
if has < canuse:
drone.amountActive = has
else:
drone.amountActive = canuse
except UnboundLocalError:
pass
return fit
class EftImportError(Exception):
"""Exception class emitted and consumed by EFT importer internally."""
...
def _importPrepare(lines):
lines = lines.splitlines()
for i in range(len(lines)):
lines[i] = lines[i].strip()
while lines and not lines[0]:
del lines[0]
while lines and not lines[-1]:
del lines[-1]
return lines
def _importSectionIter(lines):
section = Section()
for line in lines:
if not line:
if section.lines:
yield section
section = Section()
else:
section.lines.append(line)
if section.lines:
yield section
def _clearTail(lst):
while lst and lst[-1] is None:
del lst[-1]
def _importCreateFit(lines):
"""Create fit and set top-level entity (ship or citadel)."""
fit = Fit()
header = lines.pop(0)
m = re.match("\[(?P<shipType>[\w\s]+),\s*(?P<fitName>.+)\]", header)
if not m:
logging.warning("service.port.eft.importEft: corrupted fit header")
raise EftImportError
shipType = m.group("shipType").strip()
fitName = m.group("fitName").strip()
try:
ship = queries.getItem(shipType)
try:
fit.ship = Ship(ship)
except ValueError:
fit.ship = Citadel(ship)
fit.name = fitName
except:
logging.warning(
"service.port.eft.importEft: exception caught when parsing header"
)
raise EftImportError
return fit
def activeStateLimit(itemIdentity):
item = eos.db.getItem(itemIdentity.ID)
if {
"moduleBonusAssaultDamageControl",
"moduleBonusIndustrialInvulnerability",
"microJumpDrive",
"microJumpPortalDrive",
}.intersection(item.effects):
return FittingModuleState.ONLINE
return FittingModuleState.ACTIVE
def parseMutant(lines):
# Fetch base item type
try:
baseItemName = lines[0]
except IndexError:
return None
baseItem = queries.getItem(baseItemName.strip())
if baseItem is None:
return None, None, {}
# Fetch mutaplasmid item type and actual item
try:
mutaplasmidName = lines[1]
except IndexError:
return baseItem, None, {}
mutaplasmidItem = queries.getItem(mutaplasmidName.strip())
if mutaplasmidItem is None:
return baseItem, None, {}
mutaplasmidItem = getDynamicItem(mutaplasmidItem.ID)
# Process mutated attribute values
try:
mutationsLine = lines[2]
except IndexError:
return baseItem, mutaplasmidItem, {}
mutations = {}
pairs = [p.strip() for p in mutationsLine.split(",")]
for pair in pairs:
try:
attrName, value = pair.split(" ")
except ValueError:
continue
try:
value = float(value)
except (ValueError, TypeError):
continue
attrInfo = getAttributeInfo(attrName.strip())
if attrInfo is None:
continue
mutations[attrInfo.ID] = value
return baseItem, mutaplasmidItem, mutations
mutantHeaderPattern = re.compile("^\[(?P<ref>\d+)\](?P<tail>.*)")
def _importGetMutationData(lines):
data = {}
# Format: {ref: [lines]}
mutaLinesMap = {}
currentMutaRef = None
currentMutaLines = []
consumedIndices = set()
def completeMutaLines():
if currentMutaRef is not None and currentMutaLines:
mutaLinesMap[currentMutaRef] = currentMutaLines
for i, line in enumerate(lines):
m = mutantHeaderPattern.match(line)
# Start and reset at header line
if m:
completeMutaLines()
currentMutaRef = int(m.group("ref"))
currentMutaLines = []
currentMutaLines.append(m.group("tail"))
consumedIndices.add(i)
# Reset at blank line
elif not line:
completeMutaLines()
currentMutaRef = None
currentMutaLines = []
elif currentMutaRef is not None:
currentMutaLines.append(line)
consumedIndices.add(i)
else:
completeMutaLines()
# Clear mutant info from source
for i in sorted(consumedIndices, reverse=True):
del lines[i]
# Run parsing
data = {}
for ref, mutaLines in mutaLinesMap.items():
_, mutaType, mutaAttrs = parseMutant(mutaLines)
data[ref] = (mutaType, mutaAttrs)
return data
class AbstractFit:
def __init__(self):
# Modules
self.modulesHigh = []
self.modulesMed = []
self.modulesLow = []
self.rigs = []
self.subsystems = []
self.services = []
# Non-modules
self.implants = []
self.boosters = []
self.drones = {} # Format: {item: Drone}
self.fighters = []
self.cargo = {} # Format: {item: Cargo}
# Other stuff
self.mutations = (
{}
) # Format: {reference: (mutaplamid item, {attr ID: attr value})}
@property
def __slotContainerMap(self):
return {
FittingSlot.HIGH: self.modulesHigh,
FittingSlot.MED: self.modulesMed,
FittingSlot.LOW: self.modulesLow,
FittingSlot.RIG: self.rigs,
FittingSlot.SUBSYSTEM: self.subsystems,
FittingSlot.SERVICE: self.services,
}
def getContainerBySlot(self, slotType):
return self.__slotContainerMap.get(slotType)
def getSlotByContainer(self, container):
slotType = None
for k, v in self.__slotContainerMap.items():
if v is container:
slotType = k
break
return slotType
def addModules(self, itemSpecs):
modules = []
slotTypes = set()
for itemSpec in itemSpecs:
if itemSpec is None:
modules.append(None)
continue
m = self.__makeModule(itemSpec)
if m is None:
modules.append(None)
continue
modules.append(m)
slotTypes.add(m.slot)
_clearTail(modules)
# If all the modules have same slot type, put them to appropriate
# container with stubs
if len(slotTypes) == 1:
slotType = tuple(slotTypes)[0]
self.getContainerBySlot(slotType).extend(modules)
# Otherwise, put just modules
else:
for m in modules:
if m is None:
continue
self.getContainerBySlot(m.slot).append(m)
def addModule(self, itemSpec):
if itemSpec is None:
return
m = self.__makeModule(itemSpec)
if m is not None:
self.getContainerBySlot(m.slot).append(m)
def __makeModule(self, itemSpec):
# Mutate item if needed
m = None
if itemSpec.mutationIdx in self.mutations:
mutaItem, mutaAttrs = self.mutations[itemSpec.mutationIdx]
mutaplasmid = getDynamicItem(mutaItem.ID)
if mutaplasmid:
try:
m = Module(mutaplasmid.resultingItem, itemSpec.item, mutaplasmid)
except ValueError:
pass
else:
for attrID, mutator in m.mutators.items():
if attrID in mutaAttrs:
mutator.value = mutaAttrs[attrID]
# If we still don't have item (item is not mutated or we
# failed to construct mutated item), try to make regular item
if m is None:
try:
m = Module(itemSpec.item)
except ValueError:
return None
if itemSpec.charge is not None and m.isValidCharge(itemSpec.charge):
m.charge = itemSpec.charge
if itemSpec.offline and m.isValidState(FittingModuleState.OFFLINE):
m.state = FittingModuleState.OFFLINE
elif m.isValidState(FittingModuleState.ACTIVE):
m.state = activeStateLimit(m.item)
return m
def addImplant(self, itemSpec):
if itemSpec is None:
return
if "implantness" in itemSpec.item.attributes:
self.implants.append(Implant(itemSpec.item))
elif "boosterness" in itemSpec.item.attributes:
self.boosters.append(Booster(itemSpec.item))
else:
pyfalog.error("Failed to import implant: {}", itemSpec.typeName)
def addDrone(self, itemSpec):
if itemSpec is None:
return
if itemSpec.item not in self.drones:
self.drones[itemSpec.item] = Drone(itemSpec.item)
self.drones[itemSpec.item].amount += itemSpec.amount
def addFighter(self, itemSpec):
if itemSpec is None:
return
fighter = Fighter(itemSpec.item)
fighter.amount = itemSpec.amount
self.fighters.append(fighter)
def addCargo(self, itemSpec):
if itemSpec is None:
return
if itemSpec.item not in self.cargo:
self.cargo[itemSpec.item] = Cargo(itemSpec.item)
self.cargo[itemSpec.item].amount += itemSpec.amount
class Section:
def __init__(self):
self.lines = []
self.itemSpecs = []
self.__itemDataCats = None
@property
def itemDataCats(self):
if self.__itemDataCats is None:
cats = set()
for itemSpec in self.itemSpecs:
if itemSpec is None:
continue
cats.add(itemSpec.item.category.name)
self.__itemDataCats = tuple(sorted(cats))
return self.__itemDataCats
@property
def isModuleRack(self):
return all(i is None or i.isModule for i in self.itemSpecs)
@property
def isImplantRack(self):
return all(i is not None and i.isImplant for i in self.itemSpecs)
@property
def isDroneBay(self):
return all(i is not None and i.isDrone for i in self.itemSpecs)
@property
def isFighterBay(self):
return all(i is not None and i.isFighter for i in self.itemSpecs)
@property
def isCargoHold(self):
return (
all(i is not None and i.isCargo for i in self.itemSpecs)
and not self.isDroneBay
and not self.isFighterBay
)
class BaseItemSpec:
def __init__(self, typeName):
item = queries.getItem(typeName)
if item is None:
raise EftImportError
self.typeName = typeName
self.item = item
@property
def isModule(self):
return False
@property
def isImplant(self):
return False
@property
def isDrone(self):
return False
@property
def isFighter(self):
return False
@property
def isCargo(self):
return False
class RegularItemSpec(BaseItemSpec):
def __init__(self, typeName, chargeName=None):
super().__init__(typeName)
self.charge = self.__fetchCharge(chargeName)
self.offline = False
self.mutationIdx = None
def __fetchCharge(self, chargeName):
if chargeName:
charge = queries.getItem(chargeName)
if not charge or charge.category.name != "Charge":
charge = None
else:
charge = None
return charge
@property
def isModule(self):
return self.item.category.name in MODULE_CATS
@property
def isImplant(self):
return self.item.category.name == "Implant" and (
"implantness" in self.item.attributes
or "boosterness" in self.item.attributes
)
class MultiItemSpec(BaseItemSpec):
def __init__(self, typeName):
super().__init__(typeName)
self.amount = 0
@property
def isDrone(self):
return self.item.category.name == "Drone"
@property
def isFighter(self):
return self.item.category.name == "Fighter"
@property
def isCargo(self):
return True
def setup(bot):
bot.add_cog(EFT(bot))

2
eos/__init__.py

@ -0,0 +1,2 @@
version = "0.2.3"
tag = "git"

210
eos/capSim.py

@ -0,0 +1,210 @@
import heapq
import time
from math import sqrt, exp
from functools import reduce
DAY = 24 * 60 * 60 * 1000
def lcm(a, b):
n = a * b
while b:
a, b = b, a % b
return n / a
class CapSimulator(object):
"""Entity's EVE Capacitor Simulator"""
def __init__(self):
# simulator defaults (change in instance, not here)
self.capacitorCapacity = 100
self.capacitorRecharge = 1000
# max simulated time.
self.t_max = DAY
# take reloads into account?
self.reload = False
# stagger activations of identical modules?
self.stagger = False
# scale activation duration and capNeed to values that ease the
# calculation at the cost of accuracy?
self.scale = False
# millisecond resolutions for scaling
self.scale_resolutions = (100, 50, 25, 10)
# relevant decimal digits of capacitor for LCM period optimization
self.stability_precision = 1
def scale_activation(self, duration, capNeed):
for res in self.scale_resolutions:
mod = duration % res
if mod:
if mod > res / 2.0:
mod = res - mod
else:
mod = -mod
if abs(mod) <= duration / 100.0:
# only adjust if the adjustment is less than 1%
duration += mod
capNeed += float(mod) / duration * capNeed
break
return duration, capNeed
def init(self, modules):
"""prepare modules. a list of (duration, capNeed, clipSize, disableStagger) tuples is
expected, with clipSize 0 if the module has infinite ammo.
"""
self.modules = modules
def reset(self):
"""Reset the simulator state"""
self.state = []
mods = {}
period = 1
disable_period = False
# Loop over modules, clearing clipSize if applicable, and group modules based on attributes
for (duration, capNeed, clipSize, disableStagger, reloadTime) in self.modules:
if self.scale:
duration, capNeed = self.scale_activation(duration, capNeed)
# set clipSize to infinite if reloads are disabled unless it's
# a cap booster module.
if not self.reload and capNeed > 0:
clipSize = 0
reloadTime = 0
# Group modules based on their properties
if (duration, capNeed, clipSize, disableStagger, reloadTime) in mods:
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] += 1
else:
mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1
# Loop over grouped modules, configure staggering and push to the simulation state
for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items():
if self.stagger and not disableStagger:
if clipSize == 0:
duration = int(duration / amount)
else:
stagger_amount = (duration * clipSize + reloadTime) / (amount * clipSize)
for i in range(1, amount):
heapq.heappush(self.state,
[i * stagger_amount, duration,
capNeed, 0, clipSize, reloadTime])
else:
capNeed *= amount
period = lcm(period, duration)
# period optimization doesn't work when reloads are active.
if clipSize:
disable_period = True
heapq.heappush(self.state, [0, duration, capNeed, 0, clipSize, reloadTime])
if disable_period:
self.period = self.t_max
else:
self.period = period
def run(self):
"""Run the simulation"""
start = time.time()
self.reset()
push = heapq.heappush
pop = heapq.heappop
state = self.state
stability_precision = self.stability_precision
period = self.period
iterations = 0
capCapacity = self.capacitorCapacity
tau = self.capacitorRecharge / 5.0
cap_wrap = capCapacity # cap value at last period
cap_lowest = capCapacity # lowest cap value encountered
cap_lowest_pre = capCapacity # lowest cap value before activations
cap = capCapacity # current cap value
t_wrap = self.period # point in time of next period
t_last = 0
t_max = self.t_max
while 1:
activation = pop(state)
t_now, duration, capNeed, shot, clipSize, reloadTime = activation
if t_now >= t_max:
break
cap = ((1.0 + (sqrt(cap / capCapacity) - 1.0) * exp((t_last - t_now) / tau)) ** 2) * capCapacity
if t_now != t_last:
if cap < cap_lowest_pre:
cap_lowest_pre = cap
if t_now == t_wrap:
# history is repeating itself, so if we have more cap now than last
# time this happened, it is a stable setup.
if cap >= cap_wrap:
break
cap_wrap = round(cap, stability_precision)
t_wrap += period
cap -= capNeed
if cap > capCapacity:
cap = capCapacity
iterations += 1
t_last = t_now
if cap < cap_lowest:
if cap < 0.0:
break
cap_lowest = cap
# queue the next activation of this module
t_now += duration
shot += 1
if clipSize:
if shot % clipSize == 0:
shot = 0
t_now += reloadTime # include reload time
activation[0] = t_now
activation[3] = shot
push(state, activation)
push(state, activation)
# update instance with relevant results.
self.t = t_last
self.iterations = iterations
# calculate EVE's stability value
try:
avgDrain = reduce(float.__add__, [x[2] / x[1] for x in self.state], 0.0)
self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2
except ValueError:
self.cap_stable_eve = 0.0
if cap > 0.0:
# capacitor low/high water marks
self.cap_stable_low = cap_lowest
self.cap_stable_high = cap_lowest_pre
else:
self.cap_stable_low = \
self.cap_stable_high = 0.0
self.runtime = time.time() - start

32
eos/config.py

@ -0,0 +1,32 @@
import sys
from os.path import realpath, join, dirname, abspath
from logbook import Logger
import os
istravis = os.environ.get('TRAVIS') == 'true'
pyfalog = Logger(__name__)
debug = False
gamedataCache = True
saveddataCache = True
gamedata_version = ""
gamedata_date = ""
gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), ".", "eve.db"))
pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring)
if istravis is True or hasattr(sys, '_called_from_test'):
# Running in Travis. Run saveddata database in memory.
saveddata_connectionstring = 'sqlite:///:memory:'
else:
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), ".", "saveddata.db"))
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
settings = {
"useStaticAdaptiveArmorHardener": False,
"strictSkillLevels": True,
"globalDefaultSpoolupPercentage": 1.0
}
# Autodetect path, only change if the autodetection bugs out.
path = dirname(__file__)

103
eos/const.py

@ -0,0 +1,103 @@
# =============================================================================
# Copyright (C) 2019 Ryan Holmes
#
# 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/>.
# =============================================================================
from enum import IntEnum,unique
@unique
class FittingSlot(IntEnum):
"""
Contains slots for ship fittings
"""
# These are self-explanatory
LOW = 1
MED = 2
HIGH = 3
RIG = 4
SUBSYSTEM = 5
# not a real slot, need for pyfa display rack separation
MODE = 6
# system effects. They are projected "modules" and pyfa assumes all modules
# have a slot. In this case, make one up.
SYSTEM = 7
# used for citadel services
SERVICE = 8
# fighter 'slots'. Just easier to put them here...
F_LIGHT = 10
F_SUPPORT = 11
F_HEAVY = 12
# fighter 'slots' (for structures)
FS_LIGHT = 13
FS_SUPPORT = 14
FS_HEAVY = 15
@unique
class ImplantLocation(IntEnum):
"""
Contains location of the implant
"""
FIT = 0
CHARACTER = 1
@unique
class CalcType(IntEnum):
"""
Contains location of the calculation
"""
LOCAL = 0
PROJECTED = 1
COMMAND = 2
@unique
class FittingModuleState(IntEnum):
"""
Contains the state of a fitting module
"""
OFFLINE = -1
ONLINE = 0
ACTIVE = 1
OVERHEATED = 2
@unique
class FittingHardpoint(IntEnum):
"""
Contains the types of a fitting hardpoint
"""
NONE = 0
MISSILE = 1
TURRET = 2
@unique
class SpoolType(IntEnum):
SCALE = 0 # [0..1]
TIME = 1 # Expressed via time in seconds since spool up started
CYCLES = 2 # Expressed in amount of cycles since spool up started
@unique
class FitSystemSecurity(IntEnum):
HISEC = 0
LOWSEC = 1
NULLSEC = 2
WSPACE = 3

101
eos/db/__init__.py

@ -0,0 +1,101 @@
# ===============================================================================
# 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/>.
# ===============================================================================
import threading
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker
from . import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
pyfalog.info("Initializing database")
pyfalog.info("Gamedata connection: {0}", config.gamedata_connectionstring)
pyfalog.info("Saveddata connection: {0}", config.saveddata_connectionstring)
class ReadOnlyException(Exception):
pass
gamedata_connectionstring = config.gamedata_connectionstring
if callable(gamedata_connectionstring):
gamedata_engine = create_engine("sqlite://", creator=gamedata_connectionstring, echo=config.debug)
else:
gamedata_engine = create_engine(gamedata_connectionstring, echo=config.debug)
gamedata_meta = MetaData()
gamedata_meta.bind = gamedata_engine
gamedata_session = sessionmaker(bind=gamedata_engine, autoflush=False, expire_on_commit=False)()
# This should be moved elsewhere, maybe as an actual query. Current, without try-except, it breaks when making a new
# game db because we haven't reached gamedata_meta.create_all()
try:
config.gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
config.gamedata_date = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
).fetchone()[0]
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)
config.gamedata_version = None
config.gamedata_date = None
saveddata_connectionstring = config.saveddata_connectionstring
if saveddata_connectionstring is not None:
if callable(saveddata_connectionstring):
saveddata_engine = create_engine(creator=saveddata_connectionstring, echo=config.debug)
else:
saveddata_engine = create_engine(saveddata_connectionstring, echo=config.debug)
saveddata_meta = MetaData()
saveddata_meta.bind = saveddata_engine
saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)()
else:
saveddata_meta = None
# Lock controlling any changes introduced to session
sd_lock = threading.RLock()
# Import all the definitions for all our database stuff
# noinspection PyPep8
from eos.db.gamedata import alphaClones, attribute, category, effect, group, item, marketGroup, metaData, metaGroup, queries, traits, unit, dynamicAttributes
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \
miscData, mutator, module, override, price, queries, skill, targetResists, user
# Import queries
# noinspection PyPep8
from eos.db.gamedata.queries import *
# noinspection PyPep8
from eos.db.saveddata.queries import *
# If using in memory saveddata, you'll want to reflect it so the data structure is good.
if config.saveddata_connectionstring == "sqlite:///:memory:":
saveddata_meta.create_all()
pyfalog.info("Running database out of memory.")
def rollback():
with sd_lock:
pyfalog.warning("Session rollback triggered.")
saveddata_session.rollback()

2
eos/db/gamedata/__init__.py

@ -0,0 +1,2 @@
__all__ = ["attribute", "category", "effect", "group", "metaData", "dynamicAttributes",
"item", "marketGroup", "metaGroup", "unit", "alphaClones"]

50
eos/db/gamedata/alphaClones.py

@ -0,0 +1,50 @@
# ===============================================================================
# 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 Column, String, Integer, Table, ForeignKey
from sqlalchemy.orm import relation, mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import AlphaClone, AlphaCloneSkill
alphaclones_table = Table(
"alphaClones",
gamedata_meta,
Column("alphaCloneID", Integer, primary_key=True),
Column("alphaCloneName", String),
)
alphacloneskskills_table = Table(
"alphaCloneSkills",
gamedata_meta,
Column("alphaCloneID", Integer, ForeignKey("alphaClones.alphaCloneID"), primary_key=True),
Column("typeID", Integer, primary_key=True),
Column("level", Integer),
)
mapper(AlphaClone, alphaclones_table,
properties={
"ID" : synonym("alphaCloneID"),
"skills": relation(
AlphaCloneSkill,
cascade="all,delete-orphan",
backref="clone")
})
mapper(AlphaCloneSkill, alphacloneskskills_table)

65
eos/db/gamedata/attribute.py

@ -0,0 +1,65 @@
# ===============================================================================
# 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, Float, Unicode, ForeignKey, String, Boolean
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.gamedata import Attribute, AttributeInfo, Unit
typeattributes_table = Table("dgmtypeattribs", gamedata_meta,
Column("value", Float),
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),
Column("attributeID", ForeignKey("dgmattribs.attributeID"), primary_key=True))
attributes_table = Table("dgmattribs", gamedata_meta,
Column("attributeID", Integer, primary_key=True),
Column("attributeName", String),
Column("defaultValue", Float),
Column("maxAttributeID", Integer, ForeignKey("dgmattribs.attributeID")),
Column("description", Unicode),
Column("published", Boolean),
Column("displayName", String),
Column("highIsGood", Boolean),
Column("iconID", Integer),
Column("attributeCategory", Integer),
Column("tooltipDescription", Integer),
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
mapper(Attribute, typeattributes_table,
properties={"info": relation(AttributeInfo, lazy=False)})
mapper(AttributeInfo, attributes_table,
properties={
"unit" : relation(Unit),
"ID" : synonym("attributeID"),
"name" : synonym("attributeName"),
"description": deferred(attributes_table.c.description)
})
Attribute.ID = association_proxy("info", "attributeID")
Attribute.name = association_proxy("info", "attributeName")
Attribute.description = association_proxy("info", "description")
Attribute.published = association_proxy("info", "published")
Attribute.displayName = association_proxy("info", "displayName")
Attribute.highIsGood = association_proxy("info", "highIsGood")
Attribute.iconID = association_proxy("info", "iconID")
Attribute.icon = association_proxy("info", "icon")
Attribute.unit = association_proxy("info", "unit")

38
eos/db/gamedata/category.py

@ -0,0 +1,38 @@
# ===============================================================================
# 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 Boolean, Column, Integer, String, Table
from sqlalchemy.orm import deferred, mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import Category
categories_table = Table("invcategories", gamedata_meta,
Column("categoryID", Integer, primary_key=True),
Column("categoryName", String),
Column("description", String),
Column("published", Boolean),
Column("iconID", Integer))
mapper(Category, categories_table,
properties={
"ID" : synonym("categoryID"),
"name" : synonym("categoryName"),
"description": deferred(categories_table.c.description)
})

65
eos/db/gamedata/dynamicAttributes.py

@ -0,0 +1,65 @@
# ===============================================================================
# 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 Column, Float, Integer, Table, ForeignKey
from sqlalchemy.orm import mapper, relation, synonym
from sqlalchemy.ext.associationproxy import association_proxy
from eos.db import gamedata_meta
from eos.gamedata import DynamicItem, DynamicItemAttribute, DynamicItemItem, Item
from eos.gamedata import AttributeInfo
dynamic_table = Table("mutaplasmids", gamedata_meta,
Column("typeID", ForeignKey("invtypes.typeID"), primary_key=True, index=True),
Column("resultingTypeID", ForeignKey("invtypes.typeID"), primary_key=True))
dynamicAttributes_table = Table("mutaplasmidAttributes", gamedata_meta,
Column("typeID", Integer, ForeignKey("mutaplasmids.typeID"), primary_key=True),
Column("attributeID", ForeignKey("dgmattribs.attributeID"), primary_key=True),
Column("min", Float),
Column("max", Float))
dynamicApplicable_table = Table("mutaplasmidItems", gamedata_meta,
Column("typeID", ForeignKey("mutaplasmids.typeID"), primary_key=True),
Column("applicableTypeID", ForeignKey("invtypes.typeID"), primary_key=True),)
mapper(DynamicItem, dynamic_table, properties={
"attributes": relation(DynamicItemAttribute),
"item": relation(Item, foreign_keys=[dynamic_table.c.typeID]),
"resultingItem": relation(Item, foreign_keys=[dynamic_table.c.resultingTypeID]),
"ID": synonym("typeID"),
})
mapper(DynamicItemAttribute, dynamicAttributes_table,
properties={"info": relation(AttributeInfo, lazy=False)})
mapper(DynamicItemItem, dynamicApplicable_table, properties={
"mutaplasmid": relation(DynamicItem),
})
DynamicItemAttribute.ID = association_proxy("info", "attributeID")
DynamicItemAttribute.name = association_proxy("info", "attributeName")
DynamicItemAttribute.description = association_proxy("info", "description")
DynamicItemAttribute.published = association_proxy("info", "published")
DynamicItemAttribute.displayName = association_proxy("info", "displayName")
DynamicItemAttribute.highIsGood = association_proxy("info", "highIsGood")
DynamicItemAttribute.iconID = association_proxy("info", "iconID")
DynamicItemAttribute.icon = association_proxy("info", "icon")
DynamicItemAttribute.unit = association_proxy("info", "unit")

46
eos/db/gamedata/effect.py

@ -0,0 +1,46 @@
# ===============================================================================
# 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 Column, String, Integer, Boolean, Table, ForeignKey
from sqlalchemy.orm import mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.gamedata import Effect, ItemEffect
typeeffects_table = Table("dgmtypeeffects", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),
Column("effectID", Integer, ForeignKey("dgmeffects.effectID"), primary_key=True))
effects_table = Table("dgmeffects", gamedata_meta,
Column("effectID", Integer, primary_key=True),
Column("effectName", String),
Column("description", String),
Column("published", Boolean),
Column("isAssistance", Boolean),
Column("isOffensive", Boolean),
Column("resistanceID", Integer))
mapper(Effect, effects_table,
properties={
"ID" : synonym("effectID"),
"name" : synonym("effectName"),
"description": deferred(effects_table.c.description)
})
mapper(ItemEffect, typeeffects_table)

40
eos/db/gamedata/group.py

@ -0,0 +1,40 @@
# ===============================================================================
# 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 Column, String, Integer, Boolean, ForeignKey, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred, backref
from eos.db import gamedata_meta
from eos.gamedata import Category, Group
groups_table = Table("invgroups", gamedata_meta,
Column("groupID", Integer, primary_key=True),
Column("groupName", String),
Column("description", String),
Column("published", Boolean),
Column("categoryID", Integer, ForeignKey("invcategories.categoryID")),
Column("iconID", Integer))