diff --git a/CHANGELOG.md b/CHANGELOG.md index e466d20..9d46cea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Add spell base power (`base_power`) to parser, schema and models. +- Add `slot` and `base_power` to item attributes. + ## 8.0.1 (2026-06-16) - Add support for new NPC transport template. diff --git a/tests/models/test_item.py b/tests/models/test_item.py index a25fb0e..1b9f9f4 100644 --- a/tests/models/test_item.py +++ b/tests/models/test_item.py @@ -53,6 +53,32 @@ def test_item_parser_from_article_perfect_shot(self): self.assertIn("perfect_shot", item.attributes_dict) self.assertIn("perfect_shot_range", item.attributes_dict) + def test_item_parser_from_article_slot(self): + article = Article( + article_id=1, + title="Ruby Necklace", + timestamp=datetime.datetime.fromisoformat("2018-08-20T04:33:15+00:00"), + content=load_resource("content_item_slot.txt"), + ) + + item = ItemParser.from_article(article) + + self.assertIsInstance(item, Item) + self.assertEqual("Neck", item.attributes_dict.get("slot")) + + def test_item_parser_from_article_base_power(self): + article = Article( + article_id=1, + title="Sudden Death Rune", + timestamp=datetime.datetime.fromisoformat("2018-08-20T04:33:15+00:00"), + content=load_resource("content_item_base_power.txt"), + ) + + item = ItemParser.from_article(article) + + self.assertIsInstance(item, Item) + self.assertEqual("150", item.attributes_dict.get("base_power")) + def test_item_parser_from_article_damage_reflection(self): article = Article( article_id=1, diff --git a/tests/resources/content_item_base_power.txt b/tests/resources/content_item_base_power.txt new file mode 100644 index 0000000..b99ae9c --- /dev/null +++ b/tests/resources/content_item_base_power.txt @@ -0,0 +1,33 @@ + +:''This article is about the item called Sudden Death Rune. For the spell to create them, see [[Sudden Death]].'' +{{Infobox Object|List={{{1|}}}|GetValue={{{GetValue|}}} +| name = Sudden Death Rune +| article = a +| actualname = sudden death rune +| plural = ? +| itemid = 3155 +| objectclass = Runes +| primarytype = Attack Runes +| words = adori gran mort +| implemented = 4.0 +| immobile = no +| walkable = yes +| pickupable = yes +| usable = yes +| levelrequired = 45 +| vocrequired = monks, druids, sorcerers, paladins, knights +| mlrequired = 15 +| damagetype = Death +| weight = 0.70 +| stackable = yes +| marketable = yes +| droppedby = {{Dropped By|Gravedigger|Hand of Cursed Fate|Tyrn}} +| value = 162 +| storevalue = {{Store Trades|{{Store Product|28|amount=250}}}} +| npcvalue = 0 +| npcprice = 162 +| buyfrom = Asima, Chartan, Chuckles, Fenech, Frans, Frederik, Ghorza, Gnomegica, Khanna, Mordecai, Nelly, Onfroi, Rabaz, Rachel, Rock In A Hard Place, Romir, Shiriel, Siflind, Sigurd, Talila, Tandros, Topsy, Valindara, Xodet +| sellto = -- +| basepower = 150 +| notes = Shoots a powerful [[Death Damage]] attack on a single target. +}} diff --git a/tests/resources/content_item_slot.txt b/tests/resources/content_item_slot.txt new file mode 100644 index 0000000..c91f943 --- /dev/null +++ b/tests/resources/content_item_slot.txt @@ -0,0 +1,24 @@ +{{Infobox Object|List={{{1|}}}|GetValue={{{GetValue|}}} +| name = Ruby Necklace +| article = a +| actualname = ruby necklace +| plural = ruby necklaces +| itemid = 3016 +| objectclass = Body Equipment +| primarytype = Amulets and Necklaces +| slot = Neck +| implemented = 5.1 +| immobile = no +| walkable = yes +| pickupable = yes +| weight = 5.70 +| marketable = yes +| droppedby = {{Dropped By|Biting Book|Black Knight|Dawnfire Asura|Hellgorak|Lost Soul|Sineater Inferniarch|True Dawnfire Asura}} +| value = 2,000 - 3,560 +| npcvalue = 2000 +| npcprice = 3560 +| buyfrom = Augustin, Briasol, Carina, Chantalle, Edmund, Gail, Hanna, Ishina, Iwan, Jessica, Nienna, Odemara, Oiriz, Talila, Tesha, Tezila, Valindara, Yonan +| sellto = Rashid +| notes = Only used as decoration. +| history = This was the first necklace that was once obtainable only in [[Daily Respawns]] in the [[Rain Castle]] of [[Thais]]. +}} diff --git a/tests/resources/content_spell.txt b/tests/resources/content_spell.txt index 128a66a..736d05e 100644 --- a/tests/resources/content_spell.txt +++ b/tests/resources/content_spell.txt @@ -5,6 +5,7 @@ | type = Instant | subclass = Attack | damagetype = Fire +| basepower = 45 | words = exori flam | premium = yes | mana = 20 diff --git a/tests/tests_models.py b/tests/tests_models.py index 359b491..f8657bd 100644 --- a/tests/tests_models.py +++ b/tests/tests_models.py @@ -241,12 +241,14 @@ def test_spell(self): content=load_resource("content_spell.txt")) spell = models.Spell.from_article(article) self.assertIsInstance(spell, models.Spell) + self.assertEqual(spell.base_power, 45) spell.insert(self.conn) db_spell = models.Spell.get_one_by_field(self.conn, "article_id", 1) self.assertIsInstance(db_spell, models.Spell) self.assertEqual(db_spell.name, spell.name) + self.assertEqual(db_spell.base_power, spell.base_power) def test_world(self): article = Article(1, "Mortera", timestamp="2018-08-20T04:33:15Z", diff --git a/tests/tests_parsers.py b/tests/tests_parsers.py index c230622..5ef288f 100644 --- a/tests/tests_parsers.py +++ b/tests/tests_parsers.py @@ -3,8 +3,8 @@ from tests import load_resource from tibiawikisql.api import Article -from tibiawikisql.models import Achievement -from tibiawikisql.parsers import AchievementParser +from tibiawikisql.models import Achievement, Spell +from tibiawikisql.parsers import AchievementParser, SpellParser class TestParsers(unittest.TestCase): @@ -20,4 +20,17 @@ def test_achievement_parser_success(self): self.assertIsInstance(achievement, Achievement) + def test_spell_parser_success(self): + article = Article( + article_id=1, + title="Flame Strike", + timestamp=datetime.datetime.fromisoformat("2018-08-20T04:33:15+00:00"), + content=load_resource("content_spell.txt"), + ) + + spell = SpellParser.from_article(article) + + self.assertIsInstance(spell, Spell) + self.assertEqual(spell.base_power, 45) + diff --git a/tibiawikisql/models/spell.py b/tibiawikisql/models/spell.py index 1be3c9a..f598fb8 100644 --- a/tibiawikisql/models/spell.py +++ b/tibiawikisql/models/spell.py @@ -24,6 +24,8 @@ class Spell(WikiEntry, WithVersion, WithStatus, WithImage, RowModel, table=Spell """The group of the rune created by this spell.""" element: str | None = Field(None) """The element of the damage made by the spell.""" + base_power: int | None = Field(None) + """The spell's base power, used to determine its damage or healing.""" mana: int """The mana cost of the spell.""" soul: int diff --git a/tibiawikisql/parsers/item.py b/tibiawikisql/parsers/item.py index 8d94486..7c9a295 100644 --- a/tibiawikisql/parsers/item.py +++ b/tibiawikisql/parsers/item.py @@ -98,6 +98,8 @@ class ItemParser(BaseParser): "is_rotatable": "rotatable", "augments": "augments", "elemental_bond": "elementalbond", + "slot": "slot", + "base_power": "basepower", } @classmethod diff --git a/tibiawikisql/parsers/spell.py b/tibiawikisql/parsers/spell.py index 7e3ebec..7d0f8fd 100644 --- a/tibiawikisql/parsers/spell.py +++ b/tibiawikisql/parsers/spell.py @@ -23,6 +23,7 @@ class SpellParser(BaseParser): "group_secondary": AttributeParser.optional("secondarygroup"), "group_rune": AttributeParser.optional("runegroup"), "element": AttributeParser.optional("damagetype"), + "base_power": AttributeParser.optional("basepower", parse_integer), "mana": AttributeParser.optional("mana", parse_integer), "soul": AttributeParser.optional("soul", parse_integer, 0), "cooldown": AttributeParser.required("cooldown"), diff --git a/tibiawikisql/schema.py b/tibiawikisql/schema.py index 0f3eb90..4e62a6d 100644 --- a/tibiawikisql/schema.py +++ b/tibiawikisql/schema.py @@ -340,6 +340,7 @@ class SpellTable(Table): group_secondary = Column(Text, index=True) group_rune = Column(Text, index=True) element = Column(Text, index=True) + base_power = Column(Integer) level = Column(Integer) mana = Column(Integer) soul = Column(Integer, default=0)