Skip to content

Commit 687856f

Browse files
committed
add weapon proficiency perks parsing
1 parent 582a11b commit 687856f

11 files changed

Lines changed: 492 additions & 41 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Remove spell-offer generation from `Module:ItemPrices/spelldata`.
99
- Fix creature elemental modifier mapping: `energyDmgMod` now maps to `modifier_energy` and `iceDmgMod` to `modifier_ice`.
1010
- Add missing item infobox mapping for `death_attack`.
11+
- Add weapon proficiency perk generation from `Weapon Proficiency Name` and `Weapon Proficiency Tables`, using hybrid heading/template parsing and case-insensitive section-name matching.
1112

1213
## 7.0.3 (2025-07-28)
1314

docs/schema.md

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,44 @@ The generated database has the following tables.
99
## Tables
1010

1111

12-
| Table | Description |
13-
| --------------------- | ----------------------------------------------------------------------------------- |
14-
| `achievement` | Contains information for all achievements. |
15-
| `book` | Contains information about books. |
16-
| `charm` | Contains information for all charms. |
17-
| `creature` | Contains information for all creatures. |
18-
| `creature_ability` | Contains all the abilities done by creatures. |
19-
| `creature_drop` | Contains all the items dropped by creatures. |
20-
| `creature_max_damage` | Contains the breakdown of max damage done by creatures. |
21-
| `creature_sound` | Contains all the sounds made by creatures. |
22-
| `database_info` | Contains information about the database itself. |
23-
| `game_update` | Contains information about game updates. |
24-
| `house` | Contains all houses and guildhalls. |
25-
| `imbuement` | Contains information for all imbuements. |
26-
| `imbuement_material` | Contains the item materials for imbuements. |
27-
| `item` | Contains information for all items. |
28-
| `item_attribute` | Contains extra attributes and properties of items that only apply to certain types. |
29-
| `item_key` | Contains the different key variations. |
30-
| `item_sound` | Contains all the sounds made by items. |
31-
| `item_store_offer` | Contains all offers for items in the Tibia store. |
32-
| `map` | Contains the world map’s images. |
33-
| `mount` | Contains information for all mounts. |
34-
| `npc` | Contains information for all NPCs. |
35-
| `npc_destination` | Contains all the NPCs’ travel destinations. |
36-
| `npc_job` | Contains all the NPCs’ jobs. |
37-
| `npc_offer_buy` | Contains all the NPCs’ buy offers. |
38-
| `npc_offer_sell` | Contains all the NPCs’ sell offers. |
39-
| `npc_race` | Contains all the NPCs’ races. |
40-
| `outfit` | Contains information for all outfits. |
41-
| `outfit_image` | Contains images for all outfits. |
42-
| `outfit_quest` | Contains outfit and addon rewards for quests. |
43-
| `quest` | Contains information for all quests. |
44-
| `quest_danger` | Contains creatures that can be found in a quest. |
45-
| `quest_reward` | Contains item rewards for quests. |
46-
| `rashid_position` | Contains the positions for the NPC Rashid every day of the week. |
47-
| `spell` | Contains information for all spells. |
48-
| `world` | Contains information for all worlds. |
12+
| Table | Description |
13+
| ----------------------- | ----------------------------------------------------------------------------------- |
14+
| `achievement` | Contains information for all achievements. |
15+
| `book` | Contains information about books. |
16+
| `charm` | Contains information for all charms. |
17+
| `creature` | Contains information for all creatures. |
18+
| `creature_ability` | Contains all the abilities done by creatures. |
19+
| `creature_drop` | Contains all the items dropped by creatures. |
20+
| `creature_max_damage` | Contains the breakdown of max damage done by creatures. |
21+
| `creature_sound` | Contains all the sounds made by creatures. |
22+
| `database_info` | Contains information about the database itself. |
23+
| `game_update` | Contains information about game updates. |
24+
| `house` | Contains all houses and guildhalls. |
25+
| `imbuement` | Contains information for all imbuements. |
26+
| `imbuement_material` | Contains the item materials for imbuements. |
27+
| `item` | Contains information for all items. |
28+
| `item_attribute` | Contains extra attributes and properties of items that only apply to certain types. |
29+
| `item_key` | Contains the different key variations. |
30+
| `item_sound` | Contains all the sounds made by items. |
31+
| `item_proficiency_perk` | Contains weapon proficiency perks for items. |
32+
| `item_store_offer` | Contains all offers for items in the Tibia store. |
33+
| `map` | Contains the world map’s images. |
34+
| `mount` | Contains information for all mounts. |
35+
| `npc` | Contains information for all NPCs. |
36+
| `npc_destination` | Contains all the NPCs’ travel destinations. |
37+
| `npc_job` | Contains all the NPCs’ jobs. |
38+
| `npc_offer_buy` | Contains all the NPCs’ buy offers. |
39+
| `npc_offer_sell` | Contains all the NPCs’ sell offers. |
40+
| `npc_race` | Contains all the NPCs’ races. |
41+
| `outfit` | Contains information for all outfits. |
42+
| `outfit_image` | Contains images for all outfits. |
43+
| `outfit_quest` | Contains outfit and addon rewards for quests. |
44+
| `quest` | Contains information for all quests. |
45+
| `quest_danger` | Contains creatures that can be found in a quest. |
46+
| `quest_reward` | Contains item rewards for quests. |
47+
| `rashid_position` | Contains the positions for the NPC Rashid every day of the week. |
48+
| `spell` | Contains information for all spells. |
49+
| `world` | Contains information for all worlds. |
4950

5051

5152
## Table schemas
@@ -390,6 +391,17 @@ The generated database has the following tables.
390391

391392

392393

394+
### item_proficiency_perk
395+
396+
| Column | Type | Description |
397+
| ----------------- | --------- | --------------------------------------------------------- |
398+
| item_id | `INTEGER` | The id of the item this perk belongs to. |
399+
| proficiency_level | `INTEGER` | The weapon proficiency level where this perk is unlocked. |
400+
| skill_image | `TEXT` | The name of the skill image shown for this perk. |
401+
| icon | `TEXT` | The optional icon name used in this perk. |
402+
| effect | `TEXT` | The effect text shown for this perk. |
403+
404+
393405
### map
394406

395407
| Column | Type | Description |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<noinclude>used by [[Template:Infobox Object]] for the Weapon Proficiency section</noinclude>{{#switch:{{{1|}}}
2+
| Amber Axe = Sanguine 1H Axe
3+
| Amber Cudgel = Sanguine 1H Club
4+
| Amber Rod =
5+
|#default =
6+
}}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
===Sanguine 1H Axe===
2+
{{Weapon Proficiency Table
3+
|perk_1 =
4+
{{Weapon Proficiency Button |skill_image=Axe Skill Bonus|icon=|text=+1 Axe Fighting}}
5+
|perk_2 =
6+
{{Weapon Proficiency Button |skill_image=Auto-Attack Critical Extra Damage|icon=|text=+10% critical extra damage for auto-attacks}}
7+
{{Weapon Proficiency Button |skill_image=Axe Extra Damage Auto-Attack|icon=Special Icon|text=+5% of your Axe Fighting as extra damage for auto-attacks}}
8+
{{Weapon Proficiency Button |skill_image=Malformed Missing Text|icon=|}}
9+
}}
10+
===Sanguine 1H Club===
11+
{{Weapon Proficiency Table
12+
|perk_1 =
13+
{{Weapon Proficiency Button |skill_image=Club Skill Bonus|icon=|text=+1 Club Fighting}}
14+
}}

tests/test_generation.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import datetime
2+
import sqlite3
3+
import unittest
4+
from unittest.mock import patch
5+
6+
from tests import load_resource
7+
from tibiawikisql.api import Article
8+
from tibiawikisql.generation import (
9+
WEAPON_PROFICIENCY_NAME_ARTICLE,
10+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
11+
generate_item_proficiency_perks,
12+
wiki_client,
13+
)
14+
from tibiawikisql.schema import ItemProficiencyPerkTable, ItemTable
15+
16+
17+
class TestGeneration(unittest.TestCase):
18+
def setUp(self):
19+
self.conn = sqlite3.connect(":memory:")
20+
self.conn.executescript(ItemTable.get_create_table_statement())
21+
self.conn.executescript(ItemProficiencyPerkTable.get_create_table_statement())
22+
timestamp = datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00")
23+
ItemTable.insert(self.conn, article_id=1, title="Amber Axe", timestamp=timestamp)
24+
ItemTable.insert(self.conn, article_id=2, title="Amber Cudgel", timestamp=timestamp)
25+
26+
def tearDown(self):
27+
self.conn.close()
28+
29+
@staticmethod
30+
def build_article(title: str, content: str) -> Article:
31+
return Article(
32+
article_id=9999,
33+
title=title,
34+
timestamp=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"),
35+
content=content,
36+
)
37+
38+
def test_generate_item_proficiency_perks(self):
39+
mapping_article = self.build_article(
40+
WEAPON_PROFICIENCY_NAME_ARTICLE,
41+
load_resource("content_weapon_proficiency_name.txt"),
42+
)
43+
tables_article = self.build_article(
44+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
45+
load_resource("content_weapon_proficiency_tables.txt"),
46+
)
47+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
48+
49+
with (
50+
patch.object(wiki_client, "get_article", side_effect=[mapping_article, tables_article]),
51+
patch("tibiawikisql.generation.click.echo") as mock_echo,
52+
):
53+
generate_item_proficiency_perks(self.conn, data_store)
54+
55+
rows = self.conn.execute(
56+
"SELECT item_id, proficiency_level, skill_image, icon, effect "
57+
"FROM item_proficiency_perk ORDER BY rowid",
58+
).fetchall()
59+
self.assertEqual(4, len(rows))
60+
self.assertEqual(
61+
[
62+
(1, 1, "Axe Skill Bonus", None, "+1 Axe Fighting"),
63+
(1, 2, "Auto-Attack Critical Extra Damage", None, "+10% critical extra damage for auto-attacks"),
64+
(
65+
1,
66+
2,
67+
"Axe Extra Damage Auto-Attack",
68+
"Special Icon",
69+
"+5% of your Axe Fighting as extra damage for auto-attacks",
70+
),
71+
(2, 1, "Club Skill Bonus", None, "+1 Club Fighting"),
72+
],
73+
[tuple(row) for row in rows],
74+
)
75+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
76+
self.assertIn("Parsed weapon proficiency perks", messages)
77+
78+
def test_generate_item_proficiency_perks_warn_and_continue(self):
79+
mapping_article = self.build_article(
80+
WEAPON_PROFICIENCY_NAME_ARTICLE,
81+
"""{{#switch:{{{1|}}}
82+
| Amber Axe = Sanguine 1H Axe
83+
| Amber Cudgel = Missing Section
84+
| Unknown Item = Sanguine 1H Club
85+
|#default =
86+
}}""",
87+
)
88+
tables_article = self.build_article(
89+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
90+
load_resource("content_weapon_proficiency_tables.txt"),
91+
)
92+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
93+
94+
with (
95+
patch.object(wiki_client, "get_article", side_effect=[mapping_article, tables_article]),
96+
patch("tibiawikisql.generation.click.echo") as mock_echo,
97+
):
98+
generate_item_proficiency_perks(self.conn, data_store)
99+
100+
rows = self.conn.execute(
101+
"SELECT item_id, proficiency_level, skill_image, icon, effect FROM item_proficiency_perk",
102+
).fetchall()
103+
self.assertEqual(3, len(rows))
104+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
105+
self.assertIn("Could not map 1 item names to IDs", messages)
106+
self.assertIn("Could not find 1 proficiency sections", messages)
107+
self.assertIn("Skipped 1 malformed weapon proficiency perks", messages)
108+
109+
def test_generate_item_proficiency_perks_no_title_fallback(self):
110+
mapping_article = self.build_article(
111+
WEAPON_PROFICIENCY_NAME_ARTICLE,
112+
load_resource("content_weapon_proficiency_name.txt"),
113+
)
114+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
115+
116+
def get_article_side_effect(title: str) -> Article | None:
117+
if title == WEAPON_PROFICIENCY_NAME_ARTICLE:
118+
return mapping_article
119+
return None
120+
121+
with (
122+
patch.object(wiki_client, "get_article", side_effect=get_article_side_effect) as mock_get_article,
123+
patch("tibiawikisql.generation.click.echo") as mock_echo,
124+
):
125+
generate_item_proficiency_perks(self.conn, data_store)
126+
127+
rows = self.conn.execute("SELECT COUNT(*) FROM item_proficiency_perk").fetchone()
128+
self.assertEqual(0, rows[0])
129+
self.assertEqual(
130+
[WEAPON_PROFICIENCY_NAME_ARTICLE, WEAPON_PROFICIENCY_TABLES_ARTICLE],
131+
[call.args[0] for call in mock_get_article.call_args_list],
132+
)
133+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
134+
self.assertIn("Could not fetch weapon proficiency pages", messages)
135+
136+
def test_generate_item_proficiency_perks_case_insensitive_section_match(self):
137+
timestamp = datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00")
138+
ItemTable.insert(self.conn, article_id=3, title="Stale Bread of Ancientness", timestamp=timestamp)
139+
140+
mapping_article = self.build_article(
141+
WEAPON_PROFICIENCY_NAME_ARTICLE,
142+
"""{{#switch:{{{1|}}}
143+
| Stale Bread of Ancientness = Club 1H Stale Bread of Ancientness
144+
|#default =
145+
}}""",
146+
)
147+
tables_article = self.build_article(
148+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
149+
"""===Club 1H Stale Bread Of Ancientness===
150+
{{Weapon Proficiency Table
151+
|perk_1 =
152+
{{Weapon Proficiency Button |skill_image=Club Skill Bonus|icon=|text=+1 Club Fighting}}
153+
}}""",
154+
)
155+
data_store = {"items_map": {"stale bread of ancientness": 3}}
156+
157+
with (
158+
patch.object(wiki_client, "get_article", side_effect=[mapping_article, tables_article]),
159+
patch("tibiawikisql.generation.click.echo"),
160+
):
161+
generate_item_proficiency_perks(self.conn, data_store)
162+
163+
rows = self.conn.execute(
164+
"SELECT item_id, proficiency_level, skill_image, icon, effect FROM item_proficiency_perk",
165+
).fetchall()
166+
self.assertEqual([(3, 1, "Club Skill Bonus", None, "+1 Club Fighting")], [tuple(row) for row in rows])

0 commit comments

Comments
 (0)