# Copyright 2021 Allan Galarza
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
from tibiawikisql import schema
from tibiawikisql.models import abc
astral_pattern = re.compile(r"\s*([^:]+):\s*(\d+),*")
effect_pattern = re.compile(r"Effect/([^|]+)\|([^}|]+)")
def parse_astral_sources(content: str):
"""Parse the astral sources of an imbuement.
Parameters
----------
content: A string containing astral sources.
Returns
-------
:class:`dict[str,int]`:
A dictionary containing the material name and te amount required.
"""
materials = astral_pattern.findall(content)
if materials:
return {item: int(amount) for (item, amount) in materials}
return None
effect_map = {
"Bash": "Club fighting +{}",
"Chop": "Axe fighting +{}",
"Slash": "Sword fighting +{}",
"Precision": "Distance fighting +{}",
"Blockade": "Shielding +{}",
"Epiphany": "Magic level +{}",
"Scorch": "Fire damage {}",
"Venom": "Earth damage {}",
"Frost": "Ice damage {}",
"Electrify": "Energy damage {}",
"Reap": "Death damage {}",
"Vampirism": "Life leech {}",
"Void": " Mana leech {}",
"Strike": " Critical hit damage {}",
"Lich Shroud": "Death protection {}",
"Snake Skin": "Earth protection {}",
"Quara Scale": "Ice protection {}",
"Dragon Hide": "Fire protection {}",
"Cloud Fabric": "Energy protection {}",
"Demon Presence": "Holy protection {}",
"Swiftness": "Speed +{}",
"Featherweight": "Capacity +{}",
"Vibrancy": "Remove paralysis chance {}",
}
def parse_effect(effect):
"""Parse TibiaWiki's effect template into a string effect.
Parameters
----------
effect: :class:`str`
The string containing the template.
Returns
-------
:class:`str`:
The effect string.
"""
m = effect_pattern.search(effect)
category, amount = m.groups()
try:
return effect_map[category].format(amount)
except KeyError:
return f"{category} {amount}"
def parse_slots(content):
"""Parse the list of slots.
Cleans up spaces between items.
Parameters
----------
content: :class:`str`
A string containing comma separated values.
Returns
-------
:class:`str`:
The slots string.
"""
slots = content.split(",")
return ",".join(s.strip() for s in slots)
[docs]class Imbuement(abc.Row, abc.Parseable, table=schema.Imbuement):
"""Represents an imbuement type.
Attributes
----------
article_id: :class:`int`
The id of the containing article.
title: :class:`str`
The title of the containing article.
timestamp: :class:`int`
The last time the containing article was edited.
name: :class:`str`
The name of the imbuement.
tier: :class:`str`
The tier of the imbuement.
type: :class:`str`
The imbuement's type.
category: :class:`str`
The imbuement's category.
effect: :class:`str`
The effect given by the imbuement.
slots: :class:`str`
The type of items this imbuement may be applied on.
version: :class:`str`
The client version where this imbuement was first implemented.
status: :class:`str`
The status of this imbuement the game.
image: :class:`str`
The bytes of the imbuement's image.
materials: list of :class:`ImbuementMaterial`
The materials needed for the imbuement.
"""
_map = {
"name": ("name", str.strip),
"prefix": ("tier", str.strip),
"type": ("type", str.strip),
"category": ("category", str.strip),
"effect": ("effect", parse_effect),
"implemented": ("version", str.strip),
"slots": ("slots", parse_slots),
"status": ("status", str.lower),
}
_template = "Infobox_Imbuement"
__slots__ = (
"article_id",
"title",
"timestamp",
"name",
"tier",
"type",
"category",
"effect",
"slots",
"version",
"image",
"materials",
"status",
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
[docs] @classmethod
def from_article(cls, article):
imbuement = super().from_article(article)
if imbuement is None:
return None
if "astralsources" in imbuement._raw_attributes:
materials = parse_astral_sources(imbuement._raw_attributes["astralsources"])
imbuement.materials = []
for name, amount in materials.items():
imbuement.materials.append(ImbuementMaterial(item_title=name, amount=amount,
imbuement_id=imbuement.article_id))
return imbuement
[docs] def insert(self, c):
super().insert(c)
for material in getattr(self, "materials", []):
material.insert(c)
[docs] @classmethod
def get_by_field(cls, c, field, value, use_like=False):
imbuement = super().get_by_field(c, field, value, use_like)
if imbuement is None:
return None
imbuement.materials = ImbuementMaterial.search(c, "imbuement_id", imbuement.article_id)
return imbuement
[docs]class ImbuementMaterial(abc.Row, table=schema.ImbuementMaterial):
"""Represents a item material for an imbuement.
Attributes
----------
imbuement_id: :class:`int`
The article id of the imbuement this material belongs to.
imbuement_title: :class:`str`
The title of the imbuement this material belongs to.
item_id: :class:`int`
The article id of the item material.
item_title: :class:`str`
The title of the item material.
amount: :class:`int`
The amount of items required.
"""
__slots__ = (
"imbuement_id",
"imbuement_title",
"item_id",
"item_title",
"amount",
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.item_title = kwargs.get("item_title")
self.imbuement_title = kwargs.get("imbuement_title")
[docs] def insert(self, c):
if getattr(self, "item_id", None):
super().insert(c)
else:
query = f"""INSERT INTO {self.table.__tablename__}({','.join(col.name for col in self.table.columns)})
VALUES(?, (SELECT article_id from item WHERE title = ?), ?)"""
c.execute(query, (self.imbuement_id, self.item_title, self.amount))
@classmethod
def _get_base_query(cls):
return f"""SELECT {cls.table.__tablename__}.*, imbuement.title as imbuement_title, item.title as item_title
FROM {cls.table.__tablename__}
LEFT JOIN imbuement ON imbuement.article_id = imbuement_id
LEFT JOIN item ON item.article_id = item_id"""
@classmethod
def _is_column(cls, name):
return name in cls.__slots__