This commit is contained in:
Brad 2026-01-24 13:37:49 +01:00 committed by GitHub
commit c8d9ff6b98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 4426 additions and 7 deletions

View file

@ -1,4 +1,5 @@
ACORE_ARMORY_AOWOW_URL="https://wowgaming.altervista.org/aowow"
ACORE_ARMORY_WOWHEAD_URL="https://www.wowhead.com/wotlk"
ACORE_ARMORY_WEBSITE_URL="https://mywebsite.com"
ACORE_ARMORY_WEBSITE_NAME="My Website"
ACORE_ARMORY_WEBSITE_ROOT=""
@ -22,3 +23,6 @@ ACORE_ARMORY_WORLD_DATABASE__USER="acore"
ACORE_ARMORY_WORLD_DATABASE__PASSWORD="acore"
ACORE_ARMORY_WORLD_DATABASE__DATABASE="acore_world"
ACORE_ARMORY_DB_QUERY_TIMEOUT=10000
ACORE_ARMORY_FEATURE_FLAGS__ENABLE_QUESTS_PAGE=1
ACORE_ARMORY_FEATURE_FLAGS__ENABLE_SKILLS_PAGE=1
ACORE_ARMORY_FEATURE_FLAGS__ENABLE_REPUTATION_PAGE=1

View file

@ -73,6 +73,10 @@ I also noticed that such a tool was frequently requested in the AzerothCore Disc
![Character page](readme/img/cayla.png)
![Character talents](readme/img/talents.png)
![Character achievements](readme/img/achievements.png)
![Character quests](readme/img/armory-quests.png)
![Character quest compare](readme/img/armory-questcompare.png)
![Character skills](readme/img/armory-skills.png)
![Character reputation](readme/img/armory-rep.png)
![Guild page](readme/img/guild.png)
</details>
@ -128,6 +132,7 @@ I also noticed that such a tool was frequently requested in the AzerothCore Disc
| config.json | .env | Type | Default value | Description |
|--------------------------------|----------------------------------------------------|-------------------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `aowowUrl` | `ACORE_ARMORY_AOWOW_URL` | String | `"https://wowgaming.altervista.org/aowow"` | The URL of the AoWoW database to use for tooltips and links |
| `wowheadUrl` | `ACORE_ARMORY_WOWHEAD_URL` | String | `"https://www.wowhead.com/wotlk"` | The URL of the wowhead site to use for quests and zones |
| `websiteUrl` | `ACORE_ARMORY_WEBSITE_URL` | String | `"https://mywebsite.com"` | Your website's URL. Used to redirect to the homepage on error pages |
| `websiteName` | `ACORE_ARMORY_WEBSITE_NAME` | String | `"My Website"` | Your website's name. Displayed in the redirect button on error pages |
| `websiteRoot` | `ACORE_ARMORY_WEBSITE_ROOT` | String | `""` | The root of your armory's URL. If your armory is hosted on, for example, `http://mywebsite.com/azerothcore-armory`, the `websiteRoot` value should be `"/azerothcore-armory"` |
@ -145,6 +150,9 @@ I also noticed that such a tool was frequently requested in the AzerothCore Disc
| `realms[0].charactersDatabase` | `ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__`... | Database configuration object | | Configuration for the characters database. See "Database configuration" below |
| `worldDatabase` | `ACORE_ARMORY_WORLD_DATABASE__`... | Database configuration object | | Configuration for the world database. This is shared between all realms at the moment. See "Database configuration" below |
| `dbQueryTimeout` | `ACORE_ARMORY_DB_QUERY_TIMEOUT` | Number | `10000` | The maximum duration in milliseconds of a database query before it times out |
| `featureFlags.enableQuestsPage`| `ACORE_ARMORY_FEATURE_FLAGS__ENABLE_QUESTS_PAGE` | Boolean | `true` | Feature flag to enable the quests page and quest comparison tool |
| `featureFlags.enableReputationPage`| `ACORE_ARMORY_FEATURE_FLAGS__ENABLE_REPUTATION_PAGE` | Boolean | `true` | Feature flag to enable the reputation page |
| `featureFlags.enableSkillsPage`| `ACORE_ARMORY_FEATURE_FLAGS__ENABLE_SKILLS_PAGE` | Boolean | `true` | Feature flag to enable the skills page |
</details>
<details>
@ -259,8 +267,11 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
- [X] Achievements
- [X] PvP statistics, including arena teams
- [ ] Statistics (from the achievements panel in-game)
- [ ] Reputations
- [X] Reputations (work in progress, not accurate for some races)
- [X] Skills, including Professions
- [X] Quests, including quest comparison tool
- [ ] Stats (from the character sheet, i.e. health, mana, etc)
- [X] Current Zone
- [X] Guild page
- [X] Guild emblem
- [X] Members list
@ -270,6 +281,7 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
- [ ] PvP ladder
- [X] Arena ladder
- [ ] Achievements ladder
- [X] Feature Flags to enable or disable the Reputations, Skills, and Quests pages
See the [open issues](https://github.com/r-o-b-o-t-o/azerothcore-armory/issues) for a list of suggested features and known issues.

View file

@ -1,5 +1,6 @@
{
"aowowUrl": "https://wowgaming.altervista.org/aowow",
"wowheadUrl": "https://www.wowhead.com/wotlk",
"websiteUrl": "https://mywebsite.com",
"websiteName": "My Website",
"websiteRoot": "",
@ -32,5 +33,10 @@
"password": "acore",
"database": "acore_world"
},
"dbQueryTimeout": 10000
"dbQueryTimeout": 10000,
"featureFlags": {
"enableQuestsPage": true,
"enableReputationPage": true,
"enableSkillsPage": true
}
}

2308
data/Areas.csv Normal file

File diff suppressed because it is too large Load diff

402
data/Factions.csv Normal file
View file

@ -0,0 +1,402 @@
ID,ReputationID,Name
1,-1,"PLAYER, Human"
2,-1,"PLAYER, Orc"
3,-1,"PLAYER, Dwarf"
4,-1,"PLAYER, Night Elf"
5,-1,"PLAYER, Undead"
6,-1,"PLAYER, Tauren"
7,-1,Creature
8,-1,"PLAYER, Gnome"
9,-1,"PLAYER, Troll"
14,-1,Monster
15,-1,"Defias Brotherhood"
16,-1,"Gnoll - Riverpaw"
17,-1,"Gnoll - Redridge"
18,-1,"Gnoll - Shadowhide"
19,-1,Murloc
20,-1,"Undead, Scourge"
21,1,"Booty Bay"
22,-1,"Beast - Spider"
23,-1,"Beast - Boar"
24,-1,Worgen
25,-1,Kobold
26,-1,"Troll, Bloodscalp"
27,-1,"Troll, Skullsplitter"
28,-1,Prey
29,-1,"Beast - Wolf"
30,-1,"Defias Brotherhood Traitor"
31,-1,Friendly
32,-1,Trogg
33,-1,"Troll, Frostmane"
34,-1,"Orc, Blackrock"
35,-1,Villian
36,-1,Victim
37,-1,"Beast - Bear"
38,-1,Ogre
39,-1,"Kurzen's Mercenaries"
40,-1,Escortee
41,-1,"Venture Company"
42,-1,"Beast - Raptor"
43,-1,Basilisk
44,-1,"Dragonflight, Green"
45,-1,"Lost Ones"
46,29,"Blacksmithing - Armorsmithing"
47,20,Ironforge
48,-1,"Dark Iron Dwarves"
49,-1,"Human, Night Watch"
50,-1,"Dragonflight, Red"
51,-1,"Gnoll - Mosshide"
52,-1,"Orc, Dragonmaw"
53,-1,"Gnome - Leper"
54,18,"Gnomeregan Exiles"
55,-1,Leopard
56,-1,"Scarlet Crusade"
57,-1,"Gnoll - Rothide"
58,-1,"Beast - Gorilla"
59,4,"Thorium Brotherhood"
60,-1,Naga
61,-1,Dalaran
62,-1,"Forlorn Spirit"
63,-1,Darkhowl
64,-1,Grell
65,-1,Furbolg
66,-1,"Horde Generic"
67,12,Horde
68,17,Undercity
69,21,Darnassus
70,6,Syndicate
71,-1,"Hillsbrad Militia"
72,19,Stormwind
73,-1,Demon
74,-1,Elemental
75,-1,Spirit
76,14,Orgrimmar
77,-1,Treasure
78,-1,"Gnoll - Mudsnout"
79,-1,"HIllsbrad, Southshore Mayor"
80,-1,"Dragonflight, Black"
81,16,"Thunder Bluff"
82,-1,"Troll, Witherbark"
83,23,"Leatherworking - Elemental"
84,-1,"Quilboar, Razormane"
85,-1,"Quilboar, Bristleback"
86,22,"Leatherworking - Dragonscale"
87,0,"Bloodsail Buccaneers"
88,-1,Blackfathom
89,-1,Makrura
90,-1,"Centaur, Kolkar"
91,-1,"Centaur, Galak"
92,2,"Gelkis Clan Centaur"
93,3,"Magram Clan Centaur"
94,-1,Maraudine
108,-1,Theramore
109,-1,"Quilboar, Razorfen"
110,-1,"Quilboar, Razormane 2"
111,-1,"Quilboar, Deathshead"
128,-1,Enemy
148,-1,Ambient
168,-1,"Nethergarde Caravan"
169,10,"Steamwheedle Cartel"
189,-1,"Alliance Generic"
209,-1,Nethergarde
229,-1,"Wailing Caverns"
249,-1,Silithid
269,-1,"Silvermoon Remnant"
270,51,"Zandalar Tribe"
289,30,"Blacksmithing - Weaponsmithing"
309,-1,Scorpid
310,-1,"Beast - Bat"
311,-1,Titan
329,-1,"Taskmaster Fizzule"
349,5,Ravenholdt
369,7,Gadgetzan
389,-1,"Gnomeregan Bug"
409,-1,Harpy
429,-1,"Burning Blade"
449,-1,"Shadowsilk Poacher"
450,-1,"Searing Spider"
469,11,Alliance
470,9,Ratchet
471,8,"Wildhammer Clan"
489,-1,"Goblin, Dark Iron Bar Patron"
509,53,"The League of Arathor"
510,52,"The Defilers"
511,-1,Giant
529,13,"Argent Dawn"
530,15,"Darkspear Trolls"
531,-1,"Dragonflight, Bronze"
532,-1,"Dragonflight, Blue"
549,24,"Leatherworking - Tribal"
550,26,"Engineering - Goblin"
551,25,"Engineering - Gnome"
569,33,"Blacksmithing - Hammersmithing"
570,31,"Blacksmithing - Axesmithing"
571,32,"Blacksmithing - Swordsmithing"
572,-1,"Troll, Vilebranch"
573,-1,"Southsea Freebooters"
574,34,"Caer Darrow"
575,-1,"Furbolg, Uncorrupted"
576,35,"Timbermaw Hold"
577,28,Everlook
589,27,"Wintersaber Trainers"
609,36,"Cenarion Circle"
629,-1,"Shatterspear Trolls"
630,-1,"Ravasaur Trainers"
649,-1,"Majordomo Executus"
669,-1,"Beast - Carrion Bird"
670,-1,"Beast - Cat"
671,-1,"Beast - Crab"
672,-1,"Beast - Crocilisk"
673,-1,"Beast - Hyena"
674,-1,"Beast - Owl"
675,-1,"Beast - Scorpid"
676,-1,"Beast - Tallstrider"
677,-1,"Beast - Turtle"
678,-1,"Beast - Wind Serpent"
679,-1,"Training Dummy"
689,-1,"Dragonflight, Black - Bait"
709,-1,"Battleground Neutral"
729,41,"Frostwolf Clan"
730,40,"Stormpike Guard"
749,42,"Hydraxian Waterlords"
750,-1,"Sulfuron Firelords"
769,-1,"Gizlock's Dummy"
770,-1,"Gizlock's Charm"
771,-1,Gizlock
789,-1,Moro'gai
790,-1,"Spirit Guide - Alliance"
809,44,Shen'dralar
829,-1,"Ogre (Captain Kromcrush)"
849,-1,"Spirit Guide - Horde"
869,-1,Jaedenar
889,46,"Warsong Outriders"
890,45,"Silverwing Sentinels"
891,47,"Alliance Forces"
892,48,"Horde Forces"
893,-1,"Revantusk Trolls"
909,50,"Darkmoon Faire"
910,54,"Brood of Nozdormu"
911,55,"Silvermoon City"
912,-1,"Might of Kalimdor"
914,-1,"PLAYER, Blood Elf"
915,-1,"Armies of C'Thun"
916,-1,"Silithid Attackers"
917,-1,"The Ironforge Brigade"
918,-1,"RC Enemies"
919,-1,"RC Objects"
920,-1,Red
921,-1,Blue
922,56,Tranquillien
923,-1,Farstriders
924,-1,DEPRECATED
925,-1,Sunstriders
926,-1,"Magister's Guild"
927,-1,"PLAYER, Draenei"
928,-1,"Scourge Invaders"
929,-1,"Bloodmaul Clan"
930,49,Exodar
931,-1,"Test Faction (not a real faction)"
932,58,"The Aldor"
933,60,"The Consortium"
934,62,"The Scryers"
935,39,"The Sha'tar"
936,59,"Shattrath City"
937,-1,"Troll, Forest"
938,-1,"The Omenai"
939,-1,DEPRECATED
940,-1,"The Sons of Lothar"
941,61,"The Mag'har"
942,64,"Cenarion Expedition"
943,-1,"Fel Orc"
944,-1,"Fel Orc Ghost"
945,-1,"Sons of Lothar Ghosts"
946,38,"Honor Hold"
947,37,Thrallmar
948,86,"Test Faction 2"
949,85,"Test Faction 1"
950,-1,"ToWoW - Flag"
951,-1,"ToWoW - Flag Trigger Alliance (DND)"
952,87,"Test Faction 3"
953,-1,"Test Faction 4"
954,-1,"ToWoW - Flag Trigger Horde (DND)"
955,-1,Broken
956,-1,Ethereum
957,-1,"Earth Elemental"
958,-1,"Fighting Robots"
959,-1,"Actor Good"
960,-1,"Actor Evil"
961,-1,"Stillpine Furbolg"
962,-1,"Crazed Owlkin"
963,-1,"Chess Alliance"
964,-1,"Chess Horde"
965,-1,"Monster Spar"
966,-1,"Monster Spar Buddy"
967,63,"The Violet Eye"
968,-1,Sunhawks
969,-1,"Hand of Argus"
970,65,Sporeggar
971,-1,"Fungal Giant"
972,-1,"Spore Bat"
973,-1,"Monster, Predator"
974,-1,"Monster, Prey"
975,-1,"Void Anomaly"
976,-1,"Hyjal Defenders"
977,-1,"Hyjal Invaders"
978,66,Kurenai
979,-1,"Earthen Ring"
980,43,"The Burning Crusade"
981,-1,Arakkoa
982,-1,"Zangarmarsh Banner (Alliance)"
983,-1,"Zangarmarsh Banner (Horde)"
984,-1,"Zangarmarsh Banner (Neutral)"
985,-1,"Caverns of Time - Thrall"
986,-1,"Caverns of Time - Durnholde"
987,-1,"Caverns of Time - Southshore Guards"
988,-1,"Shadow Council Covert"
989,67,"Keepers of Time"
990,57,"The Scale of the Sands"
991,-1,"Dark Portal Defender, Alliance"
992,-1,"Dark Portal Defender, Horde"
993,-1,"Dark Portal Attacker, Legion"
994,-1,"Inciter Trigger"
995,-1,"Inciter Trigger 2"
996,-1,"Inciter Trigger 3"
997,-1,"Inciter Trigger 4"
998,-1,"Inciter Trigger 5"
999,-1,"Mana Creature"
1000,-1,"Khadgar's Servant"
1001,-1,"Bladespire Clan"
1002,-1,"Ethereum Sparbuddy"
1003,-1,Protectorate
1004,-1,"Arcane Annihilator (DNR)"
1005,68,"Friendly, Hidden"
1006,-1,"Kirin'Var - Dathric"
1007,-1,"Kirin'Var - Belmara"
1008,-1,"Kirin'Var - Luminrath"
1009,-1,"Kirin'Var - Cohlien"
1010,-1,"Servant of Illidan"
1011,69,"Lower City"
1012,70,"Ashtongue Deathsworn"
1013,-1,"Spirits of Shadowmoon 1"
1014,-1,"Spirits of Shadowmoon 2"
1015,71,Netherwing
1016,-1,Wyrmcult
1017,-1,Treant
1018,-1,"Leotheras Demon I"
1019,-1,"Leotheras Demon II"
1020,-1,"Leotheras Demon III"
1021,-1,"Leotheras Demon IV"
1022,-1,"Leotheras Demon V"
1023,-1,Azaloth
1024,-1,"Rock Flayer"
1025,-1,"Flayer Hunter"
1026,-1,"Shadowmoon Shade"
1027,-1,"Legion Communicator"
1028,-1,"Ravenswood Ancients"
1029,-1,"Chess, Friendly to All Chess"
1030,-1,"Black Temple Gates - Illidari"
1031,72,"Sha'tari Skyguard"
1032,-1,"Area 52"
1033,-1,Maiev
1034,-1,"Skettis Shadowy Arakkoa"
1035,-1,"Skettis Arakkoa"
1036,-1,"Dragonmaw Enemy"
1037,88,"Alliance Vanguard"
1038,73,"Ogri'la"
1039,-1,Ravager
1040,-1,REUSE
1041,-1,Frenzy
1042,-1,"Skyguard Enemy"
1043,-1,"Skunk, Petunia"
1044,-1,"Theramore Deserter"
1045,-1,Vrykul
1046,-1,"Northsea Pirates"
1047,-1,Tuskarr
1048,-1,UNUSED
1049,-1,"Troll, Amani"
1050,74,"Valiance Expedition"
1051,-1,UNUSED
1052,75,"Horde Expedition"
1053,-1,Westguard
1054,-1,"Spotted Gryphon"
1055,-1,"Tamed Plaguehound"
1056,-1,"Vrykul (Ancient Spirit 1)"
1057,-1,"Vrykul (Ancient Siprit 2)"
1058,-1,"Vrykul (Ancient Siprit 3)"
1059,-1,"CTF - Flag - Alliance"
1060,-1,Test
1061,-1,vrykul
1062,-1,"Vrykul Gladiator"
1063,-1,"Valgarde Combatant"
1064,76,"The Taunka"
1065,-1,"Monster, Zone Force Reaction 1"
1066,-1,"Monster, Zone Force Reaction 2"
1067,77,"The Hand of Vengeance"
1068,78,"Explorers' League"
1069,-1,"Ram Racing Powerup DND"
1070,-1,"Ram Racing Trap DND"
1071,-1,"Craig's Squirrels"
1072,-1,REUSE
1073,79,"The Kalu'ak"
1074,-1,"Holiday - Water Barrel"
1075,-1,"Holiday - Generic"
1076,-1,"Iron Dwarves"
1077,80,"Shattered Sun Offensive"
1078,-1,"Fighting Vanity Pet"
1079,-1,"Murloc, Winterfin"
1080,-1,"Friendly, Force Reaction"
1081,-1,"Object, Force Reaction"
1082,82,REUSE
1083,-1,REUSE
1084,-1,"Vrykul, Sea"
1085,81,"Warsong Offensive"
1086,-1,Poacher
1087,-1,"Holiday Monster"
1088,-1,"Furbolg, Redfang"
1089,-1,"Furbolg, Frostpaw"
1090,84,"Kirin Tor"
1091,83,"The Wyrmrest Accord"
1092,-1,"Azjol-Nerub"
1093,-1,REUSE
1094,90,"The Silver Covenant"
1095,-1,"Grizzly Hills Trapper"
1096,-1,REUSE
1097,89,"Wrath of the Lich King"
1098,91,"Knights of the Ebon Blade"
1099,-1,"Wrathgate Scourge"
1100,-1,"Wrathgate Alliance"
1101,-1,"Wrathgate Horde"
1102,-1,"CTF - Flag - Horde"
1103,-1,"CTF - Flag - Neutral"
1104,92,"Frenzyheart Tribe"
1105,93,"The Oracles"
1106,94,"Argent Crusade"
1107,-1,"Troll, Drakkari"
1108,-1,"CoT Arthas"
1109,-1,"CoT Stratholme Citizen"
1110,-1,"CoT Scourge"
1111,-1,Freya
1112,-1,"Mount - Taxi - Alliance"
1113,-1,"Mount - Taxi - Horde"
1114,-1,"Mount - Taxi - Neutral"
1115,-1,"Elemental, Water"
1116,-1,"Elemental, Air"
1117,95,"Sholazar Basin"
1118,96,Classic
1119,97,"The Sons of Hodir"
1120,-1,"Iron Giants"
1121,-1,"Frost Vrykul"
1122,-1,Earthen
1123,-1,"Monster Referee"
1124,98,"The Sunreavers"
1125,-1,Hyldsmeet
1126,99,"The Frostborn"
1127,-1,"Orgrimmar (Alex Test)"
1136,100,"Tranquillien Conversion"
1137,101,"Wintersaber Conversion"
1145,-1,"Hates Everything"
1154,102,"Silver Covenant Conversion"
1155,103,"Sunreavers Conversion"
1156,104,"The Ashen Verdict"
1159,-1,"CTF - Flag - Alliance 2"
1160,-1,"CTF - Flag - Horde 2"
1 ID ReputationID Name
2 1 -1 PLAYER, Human
3 2 -1 PLAYER, Orc
4 3 -1 PLAYER, Dwarf
5 4 -1 PLAYER, Night Elf
6 5 -1 PLAYER, Undead
7 6 -1 PLAYER, Tauren
8 7 -1 Creature
9 8 -1 PLAYER, Gnome
10 9 -1 PLAYER, Troll
11 14 -1 Monster
12 15 -1 Defias Brotherhood
13 16 -1 Gnoll - Riverpaw
14 17 -1 Gnoll - Redridge
15 18 -1 Gnoll - Shadowhide
16 19 -1 Murloc
17 20 -1 Undead, Scourge
18 21 1 Booty Bay
19 22 -1 Beast - Spider
20 23 -1 Beast - Boar
21 24 -1 Worgen
22 25 -1 Kobold
23 26 -1 Troll, Bloodscalp
24 27 -1 Troll, Skullsplitter
25 28 -1 Prey
26 29 -1 Beast - Wolf
27 30 -1 Defias Brotherhood Traitor
28 31 -1 Friendly
29 32 -1 Trogg
30 33 -1 Troll, Frostmane
31 34 -1 Orc, Blackrock
32 35 -1 Villian
33 36 -1 Victim
34 37 -1 Beast - Bear
35 38 -1 Ogre
36 39 -1 Kurzen's Mercenaries
37 40 -1 Escortee
38 41 -1 Venture Company
39 42 -1 Beast - Raptor
40 43 -1 Basilisk
41 44 -1 Dragonflight, Green
42 45 -1 Lost Ones
43 46 29 Blacksmithing - Armorsmithing
44 47 20 Ironforge
45 48 -1 Dark Iron Dwarves
46 49 -1 Human, Night Watch
47 50 -1 Dragonflight, Red
48 51 -1 Gnoll - Mosshide
49 52 -1 Orc, Dragonmaw
50 53 -1 Gnome - Leper
51 54 18 Gnomeregan Exiles
52 55 -1 Leopard
53 56 -1 Scarlet Crusade
54 57 -1 Gnoll - Rothide
55 58 -1 Beast - Gorilla
56 59 4 Thorium Brotherhood
57 60 -1 Naga
58 61 -1 Dalaran
59 62 -1 Forlorn Spirit
60 63 -1 Darkhowl
61 64 -1 Grell
62 65 -1 Furbolg
63 66 -1 Horde Generic
64 67 12 Horde
65 68 17 Undercity
66 69 21 Darnassus
67 70 6 Syndicate
68 71 -1 Hillsbrad Militia
69 72 19 Stormwind
70 73 -1 Demon
71 74 -1 Elemental
72 75 -1 Spirit
73 76 14 Orgrimmar
74 77 -1 Treasure
75 78 -1 Gnoll - Mudsnout
76 79 -1 HIllsbrad, Southshore Mayor
77 80 -1 Dragonflight, Black
78 81 16 Thunder Bluff
79 82 -1 Troll, Witherbark
80 83 23 Leatherworking - Elemental
81 84 -1 Quilboar, Razormane
82 85 -1 Quilboar, Bristleback
83 86 22 Leatherworking - Dragonscale
84 87 0 Bloodsail Buccaneers
85 88 -1 Blackfathom
86 89 -1 Makrura
87 90 -1 Centaur, Kolkar
88 91 -1 Centaur, Galak
89 92 2 Gelkis Clan Centaur
90 93 3 Magram Clan Centaur
91 94 -1 Maraudine
92 108 -1 Theramore
93 109 -1 Quilboar, Razorfen
94 110 -1 Quilboar, Razormane 2
95 111 -1 Quilboar, Deathshead
96 128 -1 Enemy
97 148 -1 Ambient
98 168 -1 Nethergarde Caravan
99 169 10 Steamwheedle Cartel
100 189 -1 Alliance Generic
101 209 -1 Nethergarde
102 229 -1 Wailing Caverns
103 249 -1 Silithid
104 269 -1 Silvermoon Remnant
105 270 51 Zandalar Tribe
106 289 30 Blacksmithing - Weaponsmithing
107 309 -1 Scorpid
108 310 -1 Beast - Bat
109 311 -1 Titan
110 329 -1 Taskmaster Fizzule
111 349 5 Ravenholdt
112 369 7 Gadgetzan
113 389 -1 Gnomeregan Bug
114 409 -1 Harpy
115 429 -1 Burning Blade
116 449 -1 Shadowsilk Poacher
117 450 -1 Searing Spider
118 469 11 Alliance
119 470 9 Ratchet
120 471 8 Wildhammer Clan
121 489 -1 Goblin, Dark Iron Bar Patron
122 509 53 The League of Arathor
123 510 52 The Defilers
124 511 -1 Giant
125 529 13 Argent Dawn
126 530 15 Darkspear Trolls
127 531 -1 Dragonflight, Bronze
128 532 -1 Dragonflight, Blue
129 549 24 Leatherworking - Tribal
130 550 26 Engineering - Goblin
131 551 25 Engineering - Gnome
132 569 33 Blacksmithing - Hammersmithing
133 570 31 Blacksmithing - Axesmithing
134 571 32 Blacksmithing - Swordsmithing
135 572 -1 Troll, Vilebranch
136 573 -1 Southsea Freebooters
137 574 34 Caer Darrow
138 575 -1 Furbolg, Uncorrupted
139 576 35 Timbermaw Hold
140 577 28 Everlook
141 589 27 Wintersaber Trainers
142 609 36 Cenarion Circle
143 629 -1 Shatterspear Trolls
144 630 -1 Ravasaur Trainers
145 649 -1 Majordomo Executus
146 669 -1 Beast - Carrion Bird
147 670 -1 Beast - Cat
148 671 -1 Beast - Crab
149 672 -1 Beast - Crocilisk
150 673 -1 Beast - Hyena
151 674 -1 Beast - Owl
152 675 -1 Beast - Scorpid
153 676 -1 Beast - Tallstrider
154 677 -1 Beast - Turtle
155 678 -1 Beast - Wind Serpent
156 679 -1 Training Dummy
157 689 -1 Dragonflight, Black - Bait
158 709 -1 Battleground Neutral
159 729 41 Frostwolf Clan
160 730 40 Stormpike Guard
161 749 42 Hydraxian Waterlords
162 750 -1 Sulfuron Firelords
163 769 -1 Gizlock's Dummy
164 770 -1 Gizlock's Charm
165 771 -1 Gizlock
166 789 -1 Moro'gai
167 790 -1 Spirit Guide - Alliance
168 809 44 Shen'dralar
169 829 -1 Ogre (Captain Kromcrush)
170 849 -1 Spirit Guide - Horde
171 869 -1 Jaedenar
172 889 46 Warsong Outriders
173 890 45 Silverwing Sentinels
174 891 47 Alliance Forces
175 892 48 Horde Forces
176 893 -1 Revantusk Trolls
177 909 50 Darkmoon Faire
178 910 54 Brood of Nozdormu
179 911 55 Silvermoon City
180 912 -1 Might of Kalimdor
181 914 -1 PLAYER, Blood Elf
182 915 -1 Armies of C'Thun
183 916 -1 Silithid Attackers
184 917 -1 The Ironforge Brigade
185 918 -1 RC Enemies
186 919 -1 RC Objects
187 920 -1 Red
188 921 -1 Blue
189 922 56 Tranquillien
190 923 -1 Farstriders
191 924 -1 DEPRECATED
192 925 -1 Sunstriders
193 926 -1 Magister's Guild
194 927 -1 PLAYER, Draenei
195 928 -1 Scourge Invaders
196 929 -1 Bloodmaul Clan
197 930 49 Exodar
198 931 -1 Test Faction (not a real faction)
199 932 58 The Aldor
200 933 60 The Consortium
201 934 62 The Scryers
202 935 39 The Sha'tar
203 936 59 Shattrath City
204 937 -1 Troll, Forest
205 938 -1 The Omenai
206 939 -1 DEPRECATED
207 940 -1 The Sons of Lothar
208 941 61 The Mag'har
209 942 64 Cenarion Expedition
210 943 -1 Fel Orc
211 944 -1 Fel Orc Ghost
212 945 -1 Sons of Lothar Ghosts
213 946 38 Honor Hold
214 947 37 Thrallmar
215 948 86 Test Faction 2
216 949 85 Test Faction 1
217 950 -1 ToWoW - Flag
218 951 -1 ToWoW - Flag Trigger Alliance (DND)
219 952 87 Test Faction 3
220 953 -1 Test Faction 4
221 954 -1 ToWoW - Flag Trigger Horde (DND)
222 955 -1 Broken
223 956 -1 Ethereum
224 957 -1 Earth Elemental
225 958 -1 Fighting Robots
226 959 -1 Actor Good
227 960 -1 Actor Evil
228 961 -1 Stillpine Furbolg
229 962 -1 Crazed Owlkin
230 963 -1 Chess Alliance
231 964 -1 Chess Horde
232 965 -1 Monster Spar
233 966 -1 Monster Spar Buddy
234 967 63 The Violet Eye
235 968 -1 Sunhawks
236 969 -1 Hand of Argus
237 970 65 Sporeggar
238 971 -1 Fungal Giant
239 972 -1 Spore Bat
240 973 -1 Monster, Predator
241 974 -1 Monster, Prey
242 975 -1 Void Anomaly
243 976 -1 Hyjal Defenders
244 977 -1 Hyjal Invaders
245 978 66 Kurenai
246 979 -1 Earthen Ring
247 980 43 The Burning Crusade
248 981 -1 Arakkoa
249 982 -1 Zangarmarsh Banner (Alliance)
250 983 -1 Zangarmarsh Banner (Horde)
251 984 -1 Zangarmarsh Banner (Neutral)
252 985 -1 Caverns of Time - Thrall
253 986 -1 Caverns of Time - Durnholde
254 987 -1 Caverns of Time - Southshore Guards
255 988 -1 Shadow Council Covert
256 989 67 Keepers of Time
257 990 57 The Scale of the Sands
258 991 -1 Dark Portal Defender, Alliance
259 992 -1 Dark Portal Defender, Horde
260 993 -1 Dark Portal Attacker, Legion
261 994 -1 Inciter Trigger
262 995 -1 Inciter Trigger 2
263 996 -1 Inciter Trigger 3
264 997 -1 Inciter Trigger 4
265 998 -1 Inciter Trigger 5
266 999 -1 Mana Creature
267 1000 -1 Khadgar's Servant
268 1001 -1 Bladespire Clan
269 1002 -1 Ethereum Sparbuddy
270 1003 -1 Protectorate
271 1004 -1 Arcane Annihilator (DNR)
272 1005 68 Friendly, Hidden
273 1006 -1 Kirin'Var - Dathric
274 1007 -1 Kirin'Var - Belmara
275 1008 -1 Kirin'Var - Luminrath
276 1009 -1 Kirin'Var - Cohlien
277 1010 -1 Servant of Illidan
278 1011 69 Lower City
279 1012 70 Ashtongue Deathsworn
280 1013 -1 Spirits of Shadowmoon 1
281 1014 -1 Spirits of Shadowmoon 2
282 1015 71 Netherwing
283 1016 -1 Wyrmcult
284 1017 -1 Treant
285 1018 -1 Leotheras Demon I
286 1019 -1 Leotheras Demon II
287 1020 -1 Leotheras Demon III
288 1021 -1 Leotheras Demon IV
289 1022 -1 Leotheras Demon V
290 1023 -1 Azaloth
291 1024 -1 Rock Flayer
292 1025 -1 Flayer Hunter
293 1026 -1 Shadowmoon Shade
294 1027 -1 Legion Communicator
295 1028 -1 Ravenswood Ancients
296 1029 -1 Chess, Friendly to All Chess
297 1030 -1 Black Temple Gates - Illidari
298 1031 72 Sha'tari Skyguard
299 1032 -1 Area 52
300 1033 -1 Maiev
301 1034 -1 Skettis Shadowy Arakkoa
302 1035 -1 Skettis Arakkoa
303 1036 -1 Dragonmaw Enemy
304 1037 88 Alliance Vanguard
305 1038 73 Ogri'la
306 1039 -1 Ravager
307 1040 -1 REUSE
308 1041 -1 Frenzy
309 1042 -1 Skyguard Enemy
310 1043 -1 Skunk, Petunia
311 1044 -1 Theramore Deserter
312 1045 -1 Vrykul
313 1046 -1 Northsea Pirates
314 1047 -1 Tuskarr
315 1048 -1 UNUSED
316 1049 -1 Troll, Amani
317 1050 74 Valiance Expedition
318 1051 -1 UNUSED
319 1052 75 Horde Expedition
320 1053 -1 Westguard
321 1054 -1 Spotted Gryphon
322 1055 -1 Tamed Plaguehound
323 1056 -1 Vrykul (Ancient Spirit 1)
324 1057 -1 Vrykul (Ancient Siprit 2)
325 1058 -1 Vrykul (Ancient Siprit 3)
326 1059 -1 CTF - Flag - Alliance
327 1060 -1 Test
328 1061 -1 vrykul
329 1062 -1 Vrykul Gladiator
330 1063 -1 Valgarde Combatant
331 1064 76 The Taunka
332 1065 -1 Monster, Zone Force Reaction 1
333 1066 -1 Monster, Zone Force Reaction 2
334 1067 77 The Hand of Vengeance
335 1068 78 Explorers' League
336 1069 -1 Ram Racing Powerup DND
337 1070 -1 Ram Racing Trap DND
338 1071 -1 Craig's Squirrels
339 1072 -1 REUSE
340 1073 79 The Kalu'ak
341 1074 -1 Holiday - Water Barrel
342 1075 -1 Holiday - Generic
343 1076 -1 Iron Dwarves
344 1077 80 Shattered Sun Offensive
345 1078 -1 Fighting Vanity Pet
346 1079 -1 Murloc, Winterfin
347 1080 -1 Friendly, Force Reaction
348 1081 -1 Object, Force Reaction
349 1082 82 REUSE
350 1083 -1 REUSE
351 1084 -1 Vrykul, Sea
352 1085 81 Warsong Offensive
353 1086 -1 Poacher
354 1087 -1 Holiday Monster
355 1088 -1 Furbolg, Redfang
356 1089 -1 Furbolg, Frostpaw
357 1090 84 Kirin Tor
358 1091 83 The Wyrmrest Accord
359 1092 -1 Azjol-Nerub
360 1093 -1 REUSE
361 1094 90 The Silver Covenant
362 1095 -1 Grizzly Hills Trapper
363 1096 -1 REUSE
364 1097 89 Wrath of the Lich King
365 1098 91 Knights of the Ebon Blade
366 1099 -1 Wrathgate Scourge
367 1100 -1 Wrathgate Alliance
368 1101 -1 Wrathgate Horde
369 1102 -1 CTF - Flag - Horde
370 1103 -1 CTF - Flag - Neutral
371 1104 92 Frenzyheart Tribe
372 1105 93 The Oracles
373 1106 94 Argent Crusade
374 1107 -1 Troll, Drakkari
375 1108 -1 CoT Arthas
376 1109 -1 CoT Stratholme Citizen
377 1110 -1 CoT Scourge
378 1111 -1 Freya
379 1112 -1 Mount - Taxi - Alliance
380 1113 -1 Mount - Taxi - Horde
381 1114 -1 Mount - Taxi - Neutral
382 1115 -1 Elemental, Water
383 1116 -1 Elemental, Air
384 1117 95 Sholazar Basin
385 1118 96 Classic
386 1119 97 The Sons of Hodir
387 1120 -1 Iron Giants
388 1121 -1 Frost Vrykul
389 1122 -1 Earthen
390 1123 -1 Monster Referee
391 1124 98 The Sunreavers
392 1125 -1 Hyldsmeet
393 1126 99 The Frostborn
394 1127 -1 Orgrimmar (Alex Test)
395 1136 100 Tranquillien Conversion
396 1137 101 Wintersaber Conversion
397 1145 -1 Hates Everything
398 1154 102 Silver Covenant Conversion
399 1155 103 Sunreavers Conversion
400 1156 104 The Ashen Verdict
401 1159 -1 CTF - Flag - Alliance 2
402 1160 -1 CTF - Flag - Horde 2

151
data/Skills.csv Normal file
View file

@ -0,0 +1,151 @@
ID,CategoryId,SkillCostId,Name,SpellIcon,AltVerb,CanLink
6,7,0,Frost,188,,0
8,7,0,Fire,183,,0
26,7,0,Arms,514,,0
38,7,0,Combat,243,,0
39,7,0,Subtlety,250,,0
43,6,0,Swords,1,,0
44,6,0,Axes,1,,0
45,6,0,Bows,1,,0
46,6,0,Guns,1,,0
50,7,0,Beast Mastery,255,,0
51,7,0,Survival,257,,0
54,6,0,Maces,1,,0
55,6,0,Two-Handed Swords,1,,0
56,7,0,Holy,70,,0
78,7,0,Shadow Magic,234,,0
95,6,0,Defense,1,,0
98,10,0,Language: Common,1,,0
101,9,0,Dwarven Racial,1,,0
109,10,0,Language: Orcish,1,,0
111,10,0,Language: Dwarven,1,,0
113,10,0,Language: Darnassian,1,,0
115,10,0,Language: Taurahe,1,,0
118,6,0,Dual Wield,1,,0
124,9,0,Tauren Racial,1,,0
125,9,0,Orc Racial,1,,0
126,9,0,Night Elf Racial,1,,0
129,9,0,First Aid,504,,1
134,7,0,Feral Combat,107,,0
136,6,0,Staves,1,,0
137,10,0,Language: Thalassian,1,,0
138,10,0,Language: Draconic,1,,0
139,10,0,Language: Demon Tongue,1,,0
140,10,0,Language: Titan,1,,0
141,10,0,Language: Old Tongue,1,,0
142,9,0,Survival,1,,0
148,9,0,Horse Riding,1,,0
149,9,0,Wolf Riding,1,,0
150,9,0,Tiger Riding,1,,0
152,9,0,Ram Riding,1,,0
155,9,0,Swimming,1,,0
160,6,0,Two-Handed Maces,1,,0
162,6,0,Unarmed,1,,0
163,7,0,Marksmanship,126,,0
164,11,0,Blacksmithing,335,Modify,1
165,11,0,Leatherworking,346,Emboss,1
171,11,0,Alchemy,339,Refill,1
172,6,0,Two-Handed Axes,1,,0
173,6,0,Daggers,1,,0
176,6,0,Thrown,1,,0
182,11,0,Herbalism,345,,0
183,12,0,GENERIC (DND),1,,0
184,7,0,Retribution,555,,0
185,9,0,Cooking,1467,,1
186,11,0,Mining,2418,,0
188,7,0,Pet - Imp,1,,0
189,7,0,Pet - Felhunter,1,,0
197,11,0,Tailoring,341,Embroider,1
202,11,0,Engineering,333,Tinker,1
203,7,0,Pet - Spider,1,,0
204,7,0,Pet - Voidwalker,1,,0
205,7,0,Pet - Succubus,1,,0
206,7,0,Pet - Infernal,1,,0
207,7,0,Pet - Doomguard,1,,0
208,7,0,Pet - Wolf,1,,0
209,7,0,Pet - Cat,1,,0
210,7,0,Pet - Bear,1,,0
211,7,0,Pet - Boar,1,,0
212,7,0,Pet - Crocolisk,1,,0
213,7,0,Pet - Carrion Bird,1,,0
214,7,0,Pet - Crab,1,,0
215,7,0,Pet - Gorilla,1,,0
217,7,0,Pet - Raptor,1,,0
218,7,0,Pet - Tallstrider,1,,0
220,9,0,Racial - Undead,1,,0
226,6,0,Crossbows,1,,0
228,6,0,Wands,1,,0
229,6,0,Polearms,1,,0
236,7,0,Pet - Scorpid,1,,0
237,7,0,Arcane,125,,0
251,7,0,Pet - Turtle,1,,0
253,7,0,Assassination,514,,0
256,7,0,Fury,561,,0
257,7,0,Protection,276,,0
267,7,0,Protection,291,,0
270,7,0,Pet - Generic Hunter,1,,0
293,8,0,Plate Mail,1,,0
313,10,0,Language: Gnomish,1,,0
315,10,0,Language: Troll,1,,0
333,11,0,Enchanting,578,Enchant,1
354,7,0,Demonology,90,,0
355,7,0,Affliction,88,,0
356,9,0,Fishing,580,,0
373,7,0,Enhancement,19,,0
374,7,0,Restoration,13,,0
375,7,0,Elemental Combat,62,,0
393,11,0,Skinning,1,,0
413,8,0,Mail,1,,0
414,8,0,Leather,1,,0
415,8,0,Cloth,1,,0
433,8,0,Shield,1,,0
473,6,0,Fist Weapons,1,,0
533,9,0,Raptor Riding,1,,0
553,9,0,Mechanostrider Piloting,1,,0
554,9,0,Undead Horsemanship,1,,0
573,7,0,Restoration,962,,0
574,7,0,Balance,225,,0
593,7,0,Destruction,547,,0
594,7,0,Holy,70,,0
613,7,0,Discipline,685,,0
633,7,0,Lockpicking,1,,0
653,7,0,Pet - Bat,1,,0
654,7,0,Pet - Hyena,1,,0
655,7,0,Pet - Bird of Prey,1,,0
656,7,0,Pet - Wind Serpent,1,,0
673,10,0,Language: Gutterspeak,1,,0
713,9,0,Kodo Riding,317,,0
733,9,0,Racial - Troll,0,,0
753,9,0,Racial - Gnome,0,,0
754,9,0,Racial - Human,0,,0
755,11,0,Jewelcrafting,1768,Modify,1
756,9,0,Blood Elf Racial,0,,0
758,-1,0,Pet - Event - Remote Control,0,,0
759,10,0,Language: Draenei,0,,0
760,9,0,Draenei Racial,0,,0
761,7,0,Pet - Felguard,0,,0
762,9,0,Riding,255,,0
763,7,0,Pet - Dragonhawk,1530,,0
764,7,0,Pet - Nether Ray,1577,,0
765,7,0,Pet - Sporebat,2033,,0
766,7,0,Pet - Warp Stalker,1952,,0
767,7,0,Pet - Ravager,1585,,0
768,7,0,Pet - Serpent,2167,,0
769,7,22,Internal,1775,,0
770,7,0,Blood,2636,,0
771,7,0,Frost,2632,,0
772,7,0,Unholy,2633,,0
773,11,0,Inscription,3267,Inscribe,1
775,7,0,Pet - Moth,0,,0
776,7,0,Runeforging,2727,Engrave,0
777,7,0,Mounts,2224,,0
778,7,0,Companions,0,,0
780,7,0,Pet - Exotic Chimaera,0,,0
781,7,0,Pet - Exotic Devlisaur,0,,0
782,7,0,Pet - Ghoul,221,,0
783,7,0,Pet - Exotic Silithid,0,,0
784,7,0,Pet - Exotic Worm,0,,0
785,7,0,Pet- Wasp,0,,0
786,7,0,Pet - Exotic Rhino,0,,0
787,7,0,Pet - Exotic Core Hound,0,,0
788,5,0,Pet - Exotic Spirit Beast,0,,0
1 ID CategoryId SkillCostId Name SpellIcon AltVerb CanLink
2 6 7 0 Frost 188 0
3 8 7 0 Fire 183 0
4 26 7 0 Arms 514 0
5 38 7 0 Combat 243 0
6 39 7 0 Subtlety 250 0
7 43 6 0 Swords 1 0
8 44 6 0 Axes 1 0
9 45 6 0 Bows 1 0
10 46 6 0 Guns 1 0
11 50 7 0 Beast Mastery 255 0
12 51 7 0 Survival 257 0
13 54 6 0 Maces 1 0
14 55 6 0 Two-Handed Swords 1 0
15 56 7 0 Holy 70 0
16 78 7 0 Shadow Magic 234 0
17 95 6 0 Defense 1 0
18 98 10 0 Language: Common 1 0
19 101 9 0 Dwarven Racial 1 0
20 109 10 0 Language: Orcish 1 0
21 111 10 0 Language: Dwarven 1 0
22 113 10 0 Language: Darnassian 1 0
23 115 10 0 Language: Taurahe 1 0
24 118 6 0 Dual Wield 1 0
25 124 9 0 Tauren Racial 1 0
26 125 9 0 Orc Racial 1 0
27 126 9 0 Night Elf Racial 1 0
28 129 9 0 First Aid 504 1
29 134 7 0 Feral Combat 107 0
30 136 6 0 Staves 1 0
31 137 10 0 Language: Thalassian 1 0
32 138 10 0 Language: Draconic 1 0
33 139 10 0 Language: Demon Tongue 1 0
34 140 10 0 Language: Titan 1 0
35 141 10 0 Language: Old Tongue 1 0
36 142 9 0 Survival 1 0
37 148 9 0 Horse Riding 1 0
38 149 9 0 Wolf Riding 1 0
39 150 9 0 Tiger Riding 1 0
40 152 9 0 Ram Riding 1 0
41 155 9 0 Swimming 1 0
42 160 6 0 Two-Handed Maces 1 0
43 162 6 0 Unarmed 1 0
44 163 7 0 Marksmanship 126 0
45 164 11 0 Blacksmithing 335 Modify 1
46 165 11 0 Leatherworking 346 Emboss 1
47 171 11 0 Alchemy 339 Refill 1
48 172 6 0 Two-Handed Axes 1 0
49 173 6 0 Daggers 1 0
50 176 6 0 Thrown 1 0
51 182 11 0 Herbalism 345 0
52 183 12 0 GENERIC (DND) 1 0
53 184 7 0 Retribution 555 0
54 185 9 0 Cooking 1467 1
55 186 11 0 Mining 2418 0
56 188 7 0 Pet - Imp 1 0
57 189 7 0 Pet - Felhunter 1 0
58 197 11 0 Tailoring 341 Embroider 1
59 202 11 0 Engineering 333 Tinker 1
60 203 7 0 Pet - Spider 1 0
61 204 7 0 Pet - Voidwalker 1 0
62 205 7 0 Pet - Succubus 1 0
63 206 7 0 Pet - Infernal 1 0
64 207 7 0 Pet - Doomguard 1 0
65 208 7 0 Pet - Wolf 1 0
66 209 7 0 Pet - Cat 1 0
67 210 7 0 Pet - Bear 1 0
68 211 7 0 Pet - Boar 1 0
69 212 7 0 Pet - Crocolisk 1 0
70 213 7 0 Pet - Carrion Bird 1 0
71 214 7 0 Pet - Crab 1 0
72 215 7 0 Pet - Gorilla 1 0
73 217 7 0 Pet - Raptor 1 0
74 218 7 0 Pet - Tallstrider 1 0
75 220 9 0 Racial - Undead 1 0
76 226 6 0 Crossbows 1 0
77 228 6 0 Wands 1 0
78 229 6 0 Polearms 1 0
79 236 7 0 Pet - Scorpid 1 0
80 237 7 0 Arcane 125 0
81 251 7 0 Pet - Turtle 1 0
82 253 7 0 Assassination 514 0
83 256 7 0 Fury 561 0
84 257 7 0 Protection 276 0
85 267 7 0 Protection 291 0
86 270 7 0 Pet - Generic Hunter 1 0
87 293 8 0 Plate Mail 1 0
88 313 10 0 Language: Gnomish 1 0
89 315 10 0 Language: Troll 1 0
90 333 11 0 Enchanting 578 Enchant 1
91 354 7 0 Demonology 90 0
92 355 7 0 Affliction 88 0
93 356 9 0 Fishing 580 0
94 373 7 0 Enhancement 19 0
95 374 7 0 Restoration 13 0
96 375 7 0 Elemental Combat 62 0
97 393 11 0 Skinning 1 0
98 413 8 0 Mail 1 0
99 414 8 0 Leather 1 0
100 415 8 0 Cloth 1 0
101 433 8 0 Shield 1 0
102 473 6 0 Fist Weapons 1 0
103 533 9 0 Raptor Riding 1 0
104 553 9 0 Mechanostrider Piloting 1 0
105 554 9 0 Undead Horsemanship 1 0
106 573 7 0 Restoration 962 0
107 574 7 0 Balance 225 0
108 593 7 0 Destruction 547 0
109 594 7 0 Holy 70 0
110 613 7 0 Discipline 685 0
111 633 7 0 Lockpicking 1 0
112 653 7 0 Pet - Bat 1 0
113 654 7 0 Pet - Hyena 1 0
114 655 7 0 Pet - Bird of Prey 1 0
115 656 7 0 Pet - Wind Serpent 1 0
116 673 10 0 Language: Gutterspeak 1 0
117 713 9 0 Kodo Riding 317 0
118 733 9 0 Racial - Troll 0 0
119 753 9 0 Racial - Gnome 0 0
120 754 9 0 Racial - Human 0 0
121 755 11 0 Jewelcrafting 1768 Modify 1
122 756 9 0 Blood Elf Racial 0 0
123 758 -1 0 Pet - Event - Remote Control 0 0
124 759 10 0 Language: Draenei 0 0
125 760 9 0 Draenei Racial 0 0
126 761 7 0 Pet - Felguard 0 0
127 762 9 0 Riding 255 0
128 763 7 0 Pet - Dragonhawk 1530 0
129 764 7 0 Pet - Nether Ray 1577 0
130 765 7 0 Pet - Sporebat 2033 0
131 766 7 0 Pet - Warp Stalker 1952 0
132 767 7 0 Pet - Ravager 1585 0
133 768 7 0 Pet - Serpent 2167 0
134 769 7 22 Internal 1775 0
135 770 7 0 Blood 2636 0
136 771 7 0 Frost 2632 0
137 772 7 0 Unholy 2633 0
138 773 11 0 Inscription 3267 Inscribe 1
139 775 7 0 Pet - Moth 0 0
140 776 7 0 Runeforging 2727 Engrave 0
141 777 7 0 Mounts 2224 0
142 778 7 0 Companions 0 0
143 780 7 0 Pet - Exotic Chimaera 0 0
144 781 7 0 Pet - Exotic Devlisaur 0 0
145 782 7 0 Pet - Ghoul 221 0
146 783 7 0 Pet - Exotic Silithid 0 0
147 784 7 0 Pet - Exotic Worm 0 0
148 785 7 0 Pet- Wasp 0 0
149 786 7 0 Pet - Exotic Rhino 0 0
150 787 7 0 Pet - Exotic Core Hound 0 0
151 788 5 0 Pet - Exotic Spirit Beast 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
readme/img/armory-rep.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -14,6 +14,7 @@ import { IndexController } from "./controllers/IndexController";
import { CharacterController } from "./controllers/CharacterController";
import { GuildController } from "./controllers/GuildController";
import { ArenaController } from "./controllers/ArenaController";
import { IQuest, IQuestComparison } from './types/QuestTypes';
export class Armory {
public characterCustomization: CharacterCustomization;
@ -81,6 +82,7 @@ export class Armory {
const locals = {
aowow: this.config.aowowUrl,
wowhead: this.config.wowheadUrl,
websiteUrl: this.config.websiteUrl,
websiteName: this.config.websiteName,
websiteRoot: this.config.websiteRoot,
@ -101,6 +103,45 @@ export class Armory {
helpers: {
// eslint-disable-next-line @typescript-eslint/no-var-requires
...require("handlebars-helpers")(),
hasInProgressQuests: function(quests: IQuest[]): boolean {
return quests.some(quest => quest.status === 'In Progress');
},
getDiffClass: function(quest: IQuestComparison): string {
if (!quest.char1Status && !quest.char2Status) return '';
if (!quest.char1Status) return 'quest-missing-1';
if (!quest.char2Status) return 'quest-missing-2';
if (quest.char1Status !== quest.char2Status) return 'quest-diff';
return '';
},
getDiffCount: function(quests: IQuestComparison[]): number {
return quests.reduce((count, quest) => {
if (!quest.char1Status && quest.char2Status) return count + 1;
if (quest.char1Status && !quest.char2Status) return count + 1;
if (quest.char1Status !== quest.char2Status) return count + 1;
return count;
}, 0);
},
getActiveQuests: function(categories: { [key: string]: IQuestComparison[] }): (IQuestComparison & { category: string })[] {
const activeQuests: (IQuestComparison & { category: string })[] = [];
Object.entries(categories).forEach(([category, quests]) => {
quests.forEach(quest => {
if (quest.char1Status === 'In Progress' || quest.char2Status === 'In Progress') {
activeQuests.push({
...quest,
category
});
}
});
});
return activeQuests.sort((a, b) => {
// First sort by category
const categoryCompare = a.category.localeCompare(b.category);
if (categoryCompare !== 0) return categoryCompare;
// Then by title within same category
return a.title.localeCompare(b.title);
});
}
},
}),
);
@ -150,9 +191,19 @@ export class Armory {
await charsController.load();
app.get("/character/:realm/:name", this.wrapRoute(charsController.character.bind(charsController)));
app.get("/character/:realm/:name/talents", this.wrapRoute(charsController.talents.bind(charsController)));
if (this.config.featureFlags.enableSkillsPage) {
app.get("/character/:realm/:name/skills", this.wrapRoute(charsController.skills.bind(charsController)));
}
app.get("/character/:realm/:name/achievements", this.wrapRoute(charsController.achievements.bind(charsController)));
app.get("/character/:realm/:character/achievements/data", this.wrapRoute(charsController.achievementsData.bind(charsController)));
app.get("/character/:realm/:name/pvp", this.wrapRoute(charsController.pvp.bind(charsController)));
if (this.config.featureFlags.enableReputationPage) {
app.get("/character/:realm/:name/reputation", this.wrapRoute(charsController.reputation.bind(charsController)));
}
if (this.config.featureFlags.enableQuestsPage) {
app.get("/character/:realm/:name/quests", this.wrapRoute(charsController.quests.bind(charsController)));
app.get("/character/:realm/:name/quests/compare/:otherRealm/:otherName", this.wrapRoute(charsController.questsCompare.bind(charsController)));
}
const guildsController = new GuildController(this);
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));

View file

@ -23,8 +23,15 @@ export interface IIframeModeConfig {
url: string;
}
export interface IFeatureFlags {
enableQuestsPage: boolean;
enableReputationPage: boolean;
enableSkillsPage: boolean;
}
export class Config {
public aowowUrl: string;
public wowheadUrl: string;
public websiteUrl: string;
public websiteName: string;
public websiteRoot: string;
@ -36,6 +43,7 @@ export class Config {
public realms: IRealmConfig[];
public worldDatabase: IDatabaseConfig;
public dbQueryTimeout: number;
public featureFlags: IFeatureFlags;
private static envPrefix = "ACORE_ARMORY";
private static checkedMissingField = false;

View file

@ -4,7 +4,7 @@ import { RowDataPacket } from "mysql2/promise";
import { Armory } from "../Armory";
import { IRealmConfig } from "../Config";
import { IEmblem, Utils } from "../Utils";
import { IAchievement as IAchievementDbc } from "../data/DbcReader";
import { IAchievement as IAchievementDbc, ISkillDbc, IAreas } from "../data/DbcReader";
interface ICharacterData {
guid: number;
@ -21,6 +21,11 @@ interface ICharacterData {
playerFlags: number;
online: number;
guild: string;
map: number;
zone: number;
zoneName: string;
position_x: number;
position_y: number;
}
interface IEquipmentData {
@ -72,6 +77,33 @@ interface IArenaTeam {
emblem?: IEmblem;
}
interface ISkills {
id: number;
categoryId: number
skill: string;
value: number;
max: number ;
}
interface IReputation {
id: number;
name: string;
standing: string;
value: number;
valueInGrade: number;
max: number;
expansionId: number;
}
interface IQuest {
id: number;
title: string;
status: 'Completed' | 'In Progress';
minLevel: number;
questLevel: number;
questSortID: number;
}
const ItemClassGem = 3;
const SpellMechanicMounted = 21;
const RaceDisplayName = {
@ -101,6 +133,7 @@ const ClassDisplayName = {
export class CharacterController {
private armory: Armory;
private areaById: { [key: number]: IAreas };
private itemInventoryTypes: { [key: number]: number };
private itemIcons: { [key: number]: number };
private gemItems: { [key: number]: boolean };
@ -108,6 +141,7 @@ export class CharacterController {
private itemSocketBonuses: { [key: number]: number };
private mountSpells: number[];
private mountBySpellId: { [key: number]: IMount };
private skillById: { [key: number]: ISkillDbc };
private achievementById: { [key: number]: IAchievementDbc };
public constructor(armory: Armory) {
@ -180,6 +214,15 @@ export class CharacterController {
for await (const achievement of this.armory.dbc.achievement()) {
this.achievementById[achievement.id] = achievement;
}
this.skillById = {};
for await (const skill of this.armory.dbc.skill()) {
this.skillById[skill.id] = skill;
}
this.areaById = {};
for await (const area of this.armory.dbc.areas()) {
this.areaById[area.id] = area;
}
}
public async character(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
@ -257,6 +300,413 @@ export class CharacterController {
});
}
public async skills(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
const realm = this.armory.getRealm(realmName);
if (realm === undefined) {
// Could not find realm
return next(404);
}
const charData = await this.getCharacterData(realm, charName);
if (charData === null) {
// Could not find character
return next(404);
}
const skills = await this.getSkills(realm.name, charData.guid);
const professions = skills.filter((skill) => skill.categoryId === 11);
const secondarySkills = skills.filter((skill) => skill.categoryId === 9);
const weaponSkills = skills.filter((skill) => skill.categoryId === 6);
const classSkills = skills.filter((skill) => skill.categoryId === 7);
const armorSkills = skills.filter((skill) => skill.categoryId === 8);
const languages = skills.filter((skill) => skill.categoryId === 10);
res.render("character-skills.hbs", {
title: `Armory - ${charData.name} - Skills`,
...this.makeSharedDataObject(realm, charData),
data: {
skills: skills,
professions: professions,
secondarySkills: secondarySkills,
weaponSkills: weaponSkills,
classSkills: classSkills,
armorSkills: armorSkills,
languages: languages,
},
});
}
public async reputation(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
const realm = this.armory.getRealm(realmName);
if (realm === undefined) {
return next(404);
}
const charData = await this.getCharacterData(realm, charName);
if (charData === null) {
return next(404);
}
const reputations = await this.getReputations(realm.name, charData.guid);
// Group reputations by expansion/category
const classicReps = reputations.filter(rep => rep.expansionId === 0);
const tbcReps = reputations.filter(rep => rep.expansionId === 1);
const wotlkReps = reputations.filter(rep => rep.expansionId === 2);
res.render("character-reputation.hbs", {
title: `Armory - ${charData.name} - Reputation`,
...this.makeSharedDataObject(realm, charData),
data: {
classic: classicReps,
tbc: tbcReps,
wotlk: wotlkReps
},
});
}
public async quests(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
const realm = this.armory.getRealm(realmName);
if (realm === undefined) {
return next(404);
}
const charData = await this.getCharacterData(realm, charName);
if (charData === null) {
return next(404);
}
const quests = await this.getQuests(realm.name, charData.guid);
// Group quests by zone/profession
const questsByCategory = quests.reduce((acc, quest) => {
const category = quest.questSortID > 0 ?
this.getZoneName(quest.questSortID):
this.getProfessionName(quest.questSortID);
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(quest);
return acc;
}, {});
// Get list of all characters
const allCharacters = await this.getAllCharacters(realm.name);
res.render("character-quests.hbs", {
title: `Armory - ${charData.name} - Quests`,
...this.makeSharedDataObject(realm, charData),
data: {
categories: questsByCategory,
otherCharacters: allCharacters.filter(c => c.guid !== charData.guid)
},
});
}
public async questsCompare(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
const otherRealmName = req.params.otherRealm;
const otherCharName = req.params.otherName;
const realm = this.armory.getRealm(realmName);
const otherRealm = this.armory.getRealm(otherRealmName);
if (realm === undefined || otherRealm === undefined) {
return next(404);
}
const charData = await this.getCharacterData(realm, charName);
const otherCharData = await this.getCharacterData(otherRealm, otherCharName);
if (charData === null || otherCharData === null) {
return next(404);
}
const charQuests = await this.getQuests(realm.name, charData.guid);
const otherQuests = await this.getQuests(otherRealm.name, otherCharData.guid);
// Group quests by category
interface IQuestComparison {
id: number;
title: string;
questLevel: number;
char1Status?: 'Completed' | 'In Progress';
char2Status?: 'Completed' | 'In Progress';
}
const categories: { [key: string]: IQuestComparison[] } = {};
const addQuestsToCategories = (quests: IQuest[], source: 'char1Status' | 'char2Status') => {
quests.forEach(quest => {
const category = quest.questSortID > 0 ?
this.getZoneName(quest.questSortID) :
this.getProfessionName(quest.questSortID);
if (!categories[category]) {
categories[category] = [];
}
const existingQuest = categories[category].find(q => q.id === quest.id);
if (existingQuest) {
existingQuest[source] = quest.status;
// Remove quest if both characters have completed it
if (existingQuest.char1Status === 'Completed' && existingQuest.char2Status === 'Completed') {
categories[category] = categories[category].filter(q => q.id !== quest.id);
}
} else {
categories[category].push({
id: quest.id,
title: quest.title,
questLevel: quest.questLevel,
[source]: quest.status
});
}
});
};
addQuestsToCategories(charQuests, 'char1Status');
addQuestsToCategories(otherQuests, 'char2Status');
// Get list of all characters
const allCharacters = await this.getAllCharacters(realm.name);
res.render("character-quests-compare.hbs", {
title: `Armory - Quest Compare - ${charData.name} vs ${otherCharData.name}`,
...this.makeSharedDataObject(realm, charData),
data: {
categories: categories,
char1: {
name: charData.name,
realm: realm.name
},
char2: {
name: otherCharData.name,
realm: otherRealm.name
},
otherCharacters: allCharacters.filter(c => c.guid !== charData.guid)
},
});
}
private async getReputations(realm: string, character: number): Promise<IReputation[]> {
const [rows] = await this.armory.getCharactersDb(realm).query({
sql: `
SELECT faction, standing, flags
FROM character_reputation
WHERE guid = ?
AND flags NOT IN (0, 4, 8)
AND standing != 0
`,
values: [character],
timeout: this.armory.config.dbQueryTimeout,
});
const reputations = [];
for (const row of rows as RowDataPacket[]) {
const factionInfo = await this.armory.dbc.faction().find(f => f.id === row.faction);
if (factionInfo && factionInfo.reputationId >= 0) {
reputations.push({
id: row.faction,
name: factionInfo.name,
standing: this.getReputationStanding(row.standing),
value: row.standing,
valueInGrade: this.getReputationInGrade(row.standing),
max: this.getReputationMax(row.standing),
expansionId: this.getExpansionId(row.faction)
});
}
}
return reputations;
}
private getReputationStanding(value: number): string {
if (value < -6000) return 'Hated'; // These are probably not correct, but I don't have a character to test against
if (value < -3000) return 'Hostile'; // These are probably not correct, but I don't have a character to test against
if (value < 0) return 'Unfriendly'; // These are probably not correct, but I don't have a character to test against
if (value < 1000) return 'Neutral'; // These are probably not correct, but I don't have a character to test against
if (value < 6000) return 'Friendly';
if (value < 19000) return 'Honored';
if (value < 40000) return 'Revered';
return 'Exalted'; // These are probably not correct, but I don't have a character to test against
}
private getReputationMax(value: number): number {
if (value < -6000) return -6000; // These are probably not correct, but I don't have a character to test against
if (value < -3000) return -3000; // These are probably not correct, but I don't have a character to test against
if (value < 0) return 0; // These are probably not correct, but I don't have a character to test against
if (value < 1000) return 3000;
if (value < 6000) return 6000;
if (value < 12000) return 12000;
if (value < 21000) return 21000;
return 40000; // This might not be right, but I dont have an exalted character to test against
}
private getReputationInGrade(value: number): number {
if (value < -6000) return value + 3000; // These are probably not correct, but I don't have a character to test against
if (value < -3000) return value; // These are probably not correct, but I don't have a character to test against
if (value < 0) return value; // These are probably not correct, but I don't have a character to test against
if (value < 1000) return value + 2000; // These are probably not correct, but I don't have a character to test against
if (value < 6000) return value - 1000;
if (value < 12000) return value - 5900; // For some reason I needed to remove an extra 100 from this one to get it to match the client
if (value < 21000) return value - 16900; // For some reason I needed to remove an extra 100 from this one to get it to match the client
return value - 21100; // This might not be right, but I dont have an exalted character to test against
}
private getExpansionId(factionId: number): number {
// Classic factions
if (factionId < 900) return 0;
// TBC factions
if (factionId < 1100) return 1;
// WotLK factions
return 2;
}
private async getQuests(realm: string, character: number): Promise<IQuest[]> {
// Get completed and rewarded quests
const [completedRows] = await this.armory.getCharactersDb(realm).query({
sql: `
SELECT quest FROM (
SELECT quest FROM character_queststatus
WHERE guid = ? AND status = 1
UNION
SELECT quest FROM character_queststatus_rewarded
WHERE guid = ?
) AS completed_quests
`,
values: [character, character],
timeout: this.armory.config.dbQueryTimeout,
});
// Get in progress quests
const [inProgressRows] = await this.armory.getCharactersDb(realm).query({
sql: `
SELECT quest
FROM character_queststatus
WHERE guid = ? AND status = 3
`,
values: [character],
timeout: this.armory.config.dbQueryTimeout,
});
const quests: IQuest[] = [];
// Process completed quests
for (const row of completedRows as RowDataPacket[]) {
const questInfo = await this.getQuestInfo(row.quest);
if (questInfo) {
quests.push({
id: row.quest,
title: questInfo.title,
status: 'Completed',
minLevel: questInfo.minLevel,
questLevel: questInfo.questLevel,
questSortID: questInfo.questSortID
});
}
}
// Process in progress quests
for (const row of inProgressRows as RowDataPacket[]) {
const questInfo = await this.getQuestInfo(row.quest);
if (questInfo) {
quests.push({
id: row.quest,
title: questInfo.title,
status: 'In Progress',
minLevel: questInfo.minLevel,
questLevel: questInfo.questLevel,
questSortID: questInfo.questSortID
});
}
}
return quests;
}
private async getQuestInfo(questId: number): Promise<IQuest> {
const [rows] = await this.armory.worldDb.query({
sql: `
SELECT ID, LogTitle as title, MinLevel as minLevel, QuestLevel as questLevel, QuestSortID as questSortID
FROM quest_template
WHERE ID = ?
`,
values: [questId],
timeout: this.armory.config.dbQueryTimeout,
});
return rows[0];
}
private getZoneName(zoneId: number): string {
this.areaById[zoneId]?.zoneName;
return this.areaById[zoneId]?.zoneName || `Zone ${zoneId}`;
}
private getProfessionName(professionId: number): string {
const questTypes = {
// Negative IDs (Classes and Professions)
// Classes
"-61": "Warlock",
"-81": "Warrior",
"-82": "Shaman",
"-141": "Paladin",
"-161": "Mage",
"-162": "Rogue",
"-261": "Hunter",
"-262": "Priest",
"-263": "Druid",
"-372": "Death Knight",
// Professions
"-24": "Herbalism",
"-101": "Fishing",
"-121": "Blacksmithing",
"-181": "Alchemy",
"-182": "Leatherworking",
"-201": "Engineering",
"-264": "Tailoring",
"-304": "Cooking",
"-324": "First Aid",
"-371": "Inscription",
"-373": "Jewelcrafting",
"-762": "Riding",
// Misc
"-1": "Epic",
"-21": "Wailing Caverns",
"-22": "Seasonal",
"-23": "Undercity",
"-25": "Battlegrounds",
"-41": "Uldaman",
"-221": "Treasure Map",
"-241": "Tournament",
"-284": "Special",
"-344": "Legendary",
"-364": "Darkmoon Faire",
"-365": "Ahn'Qiraj War",
"-366": "Lunar Festival",
"-367": "Reputation",
"-368": "Invasion",
"-369": "Midsummer",
"-370": "Brewfest",
"-374": "Noblegarden",
"-375": "Pilgrim's Bounty",
"-376": "Love is in the Air"
};
return questTypes[professionId] || `Category ${professionId}`;
}
private getQuestExpansionId(questLevel: number): number {
if (questLevel <= 60) return 0; // Classic
if (questLevel <= 70) return 1; // TBC
return 2; // WotLK
}
public async achievements(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
@ -337,6 +787,11 @@ export class CharacterController {
level: charData.level,
online: charData.online === 1,
guild: charData.guild,
zone: charData.zone,
zoneName: this.getZoneName(charData.zone),
enableQuestsPage: this.armory.config.featureFlags.enableQuestsPage,
enableSkillsPage: this.armory.config.featureFlags.enableSkillsPage,
enableReputationPage: this.armory.config.featureFlags.enableReputationPage
};
}
@ -344,7 +799,7 @@ export class CharacterController {
const where = typeof character === "string" ? "LOWER(`characters`.`name`) = LOWER(?)" : "`characters`.`guid` = ?";
const [rows] = await this.armory.getCharactersDb(realm.name).query({
sql: `
SELECT \`characters\`.\`guid\`, \`characters\`.\`name\`, \`race\`, \`class\`, \`gender\`, \`level\`, \`skin\`, \`face\`, \`hairStyle\`, \`hairColor\`, \`facialStyle\`, \`playerFlags\`, \`online\`, \`guild\`.\`name\` AS \`guild\`
SELECT \`characters\`.\`guid\`, \`characters\`.\`name\`, \`race\`, \`class\`, \`gender\`, \`level\`, \`skin\`, \`face\`, \`hairStyle\`, \`hairColor\`, \`facialStyle\`, \`playerFlags\`, \`online\`, \`map\`, \`zone\`, \`position_x\`, \`position_y\`, \`guild\`.\`name\` AS \`guild\`
FROM \`characters\`
LEFT JOIN \`guild_member\` ON \`guild_member\`.\`guid\` = \`characters\`.\`guid\`
LEFT JOIN \`guild\` ON \`guild\`.\`guildid\` = \`guild_member\`.\`guildid\`
@ -968,6 +1423,31 @@ export class CharacterController {
return talents;
}
private async getSkills(realm: string, character: number): Promise<ISkills[]> {
const [rows] = await this.armory.getCharactersDb(realm).query({
sql: `
SELECT skill, value, max
FROM character_skills
WHERE guid = ?
`,
values: [character],
timeout: this.armory.config.dbQueryTimeout,
});
const skills: { id: number, categoryId: number, skill: string; value: number; max: number }[] = [];
for (const row of rows as RowDataPacket[]) {
skills.push({
id: row.skill,
categoryId: this.skillById[row.skill].categoryId,
skill: this.skillById[row.skill].name,
value: row.value,
max: row.max,
});
}
return skills;
}
private async getTalentTrees(classId: number) {
const items = await this.armory.dbc
.talentTab()
@ -1103,4 +1583,17 @@ export class CharacterController {
return row;
});
}
private async getAllCharacters(currentRealm: string): Promise<Array<{name: string, realmName: string, guid: number}>> {
const [rows] = await this.armory.getCharactersDb(currentRealm).query({
sql: 'SELECT name, guid FROM characters WHERE level > 1 order by name asc',
timeout: this.armory.config.dbQueryTimeout,
});
return (rows as RowDataPacket[]).map(row => ({
name: row.name,
guid: row.guid,
realmName: currentRealm
}));
}
}

View file

@ -65,6 +65,16 @@ export interface IMountXDisplayDbc {
mountId: number;
}
export interface ISkillDbc {
id: number;
categoryId: number;
skillCostId: number;
name: string;
spellIcon: number;
altVerb: string;
canLink: number;
}
export interface ISpellDbc {
id: number;
mechanic: number;
@ -102,6 +112,19 @@ export interface ITalentTab {
classMask: number;
}
export interface IFactionDbc {
id: number;
reputationId: number;
name: string;
}
export interface IAreas {
id: number;
zoneName: string;
mapId: number;
areaId: number;
}
interface IAsyncGeneratorWithArrayMethods<T> {
[Symbol.asyncIterator](): AsyncGenerator<T>;
toArray(): Promise<T[]>;
@ -305,6 +328,8 @@ const dir = path.join(process.cwd(), "data");
export const DbcFiles = {
achievement: path.join(dir, "Achievement_3.3.5_12340.csv"),
achievementCategory: path.join(dir, "AchievementCategory_3.3.5_12340.csv"),
areas: path.join(dir, "Areas.csv"),
faction: path.join(dir, "Factions.csv"),
glyphProperties: path.join(dir, "GlyphProperties_3.3.5_12340.csv"),
item: path.join(dir, "Item_3.3.5_12340.csv"),
itemRetail: path.join(dir, "Item_9.2.0_41462.csv"),
@ -313,6 +338,7 @@ export const DbcFiles = {
itemDisplayInfo: path.join(dir, "ItemDisplayInfo_3.3.5_12340.csv"),
mount: path.join(dir, "Mount_9.2.0_41462.csv"),
mountDisplay: path.join(dir, "MountXDisplay_9.2.0_41462.csv"),
skill: path.join(dir, "Skills.csv"),
spell: path.join(dir, "Spell_3.3.5_12340.csv"),
spellItemEnchantment: path.join(dir, "SpellItemEnchantment_3.3.5_12340.csv"),
spellIcon: path.join(dir, "SpellIcon_3.3.5_12340.csv"),
@ -323,6 +349,7 @@ export const DbcFiles = {
const dbcFields = {
achievement: ["id", "faction", "titleLang0", "descriptionLang0", "category", "points", "flags", "iconId"],
achievementCategory: ["id", "parent", "nameLang0"],
areas: ["id", "zoneName", "mapId", "areaId"],
glyphProperties: ["id", "spellId"],
item: ["id", "classId", "subclassId", "displayInfoId", "inventoryType"],
itemRetail: ["id", "inventoryType"],
@ -331,6 +358,7 @@ const dbcFields = {
itemDisplayInfo: ["id", "inventoryIcon0"],
mount: ["id", "sourceSpellId"],
mountDisplay: ["id", "creatureDisplayInfoId", "mountId"],
skill: ["id", "name"],
spell: ["id", "mechanic", "spellIconId"],
spellItemEnchantment: ["id", "srcItemId"],
spellIcon: ["id", "textureFilename"],
@ -348,12 +376,15 @@ const dbcFields = {
"prereqRank0",
],
talentTab: ["id", "nameLang0", "spellIconId", "classMask"],
faction: ["id", "reputationId", "name"],
};
export class DbcManager {
private _achievement: IAchievement[];
private _achievementCategory: IAchievementCategory[];
private _areas: IAreas[];
private _glyphProperties: IGlyphProperties[];
private _faction: IFactionDbc[];
private _item: IItemDbc[];
private _itemRetail: IItemRetailDbc[];
private _itemAppearance: IItemAppearanceDbc[];
@ -361,6 +392,7 @@ export class DbcManager {
private _itemDisplayInfo: IItemDisplayInfoDbc[];
private _mount: IMountDbc[];
private _mountDisplay: IMountXDisplayDbc[];
private _skill: ISkillDbc[];
private _spell: ISpellDbc[];
private _spellItemEnchantment: ISpellItemEnchantmentDbc[];
private _spellIcon: ISpellIcon[];
@ -373,6 +405,7 @@ export class DbcManager {
DbcFiles.achievementCategory,
dbcFields.achievementCategory,
).toArray();
this._areas = await this.read<IAreas>(DbcFiles.areas).toArray();
this._glyphProperties = await this.read<IGlyphProperties>(DbcFiles.glyphProperties, dbcFields.glyphProperties).toArray();
this._item = await this.read<IItemDbc>(DbcFiles.item, dbcFields.item).toArray();
this._itemRetail = await this.read<IItemRetailDbc>(DbcFiles.itemRetail, dbcFields.itemRetail).toArray();
@ -384,6 +417,7 @@ export class DbcManager {
this._itemDisplayInfo = await this.read<IItemDisplayInfoDbc>(DbcFiles.itemDisplayInfo, dbcFields.itemDisplayInfo).toArray();
this._mount = await this.read<IMountDbc>(DbcFiles.mount, dbcFields.mount).toArray();
this._mountDisplay = await this.read<IMountXDisplayDbc>(DbcFiles.mountDisplay, dbcFields.mountDisplay).toArray();
this._skill = await this.read<ISkillDbc>(DbcFiles.skill).toArray();
this._spell = await this.read<ISpellDbc>(DbcFiles.spell, dbcFields.spell).toArray();
this._spellItemEnchantment = await this.read<ISpellItemEnchantmentDbc>(
DbcFiles.spellItemEnchantment,
@ -402,6 +436,14 @@ export class DbcManager {
return this.getLoadedDataOrRead(DbcFiles.achievementCategory, this._achievementCategory, dbcFields.achievementCategory);
}
public areas() {
return this.getLoadedDataOrRead(DbcFiles.areas, this._areas);
}
public faction() {
return this.getLoadedDataOrRead(DbcFiles.faction, this._faction, dbcFields.faction);
}
public glyphProperties() {
return this.getLoadedDataOrRead(DbcFiles.glyphProperties, this._glyphProperties, dbcFields.glyphProperties);
}
@ -434,6 +476,10 @@ export class DbcManager {
return this.getLoadedDataOrRead(DbcFiles.mountDisplay, this._mountDisplay, dbcFields.mountDisplay);
}
public skill() {
return this.getLoadedDataOrRead(DbcFiles.skill, this._skill, dbcFields.skill);
}
public spell() {
return this.getLoadedDataOrRead(DbcFiles.spell, this._spell, dbcFields.spell);
}

View file

@ -0,0 +1,17 @@
export interface IQuest {
id: number;
title: string;
status: 'Completed' | 'In Progress';
minLevel: number;
questLevel: number;
questSortID: number;
}
export interface IQuestComparison {
id: number;
title: string;
questLevel: number;
char1Status?: 'Completed' | 'In Progress';
char2Status?: 'Completed' | 'In Progress';
category?: string;
}

View file

@ -0,0 +1,167 @@
<link rel="stylesheet" type="text/css" href="{{websiteRoot}}/css/character-quests.css">
{{> icons }}
<script type="application/javascript">
const aowow_tooltips = { "renamelinks": true, };
</script>
{{> character-header }}
<div class="quest-compare-section" style="margin-bottom: 20px;">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Compare with:</label>
</div>
<div class="field-body">
<div class="field has-addons">
<div class="control is-expanded">
<div class="select is-fullwidth">
<select id="compareCharacter">
<option value="">Select a character...</option>
{{#each data.otherCharacters}}
<option value="{{this.realmName}}/{{this.name}}"
{{#if (eq this.realmName ../data.char2.realm)}}
{{#if (eq this.name ../data.char2.name)}}
selected
{{/if}}
{{/if}}>
{{this.name}}
</option>
{{/each}}
</select>
</div>
</div>
<div class="control">
<button class="button is-info" id="compareButton" disabled>
Compare Quests
</button>
</div>
</div>
</div>
</div>
</div>
<div class="quest-comparison-header">
<h2>Quest Comparison</h2>
<div class="characters">
<span class="char1">
{{#if (eq data.char2.realm data.char1.realm)}}
{{data.char1.name}}
{{else}}
{{data.char1.realm}}/{{data.char1.name}}
{{/if}}
</span>
vs
<span class="char2">
<a href="{{websiteRoot}}/character/{{data.char2.realm}}/{{data.char2.name}}/quests/compare/{{data.char1.realm}}/{{data.char1.name}}">
{{#if (eq data.char2.realm data.char1.realm)}}
{{data.char2.name}}
{{else}}
{{data.char2.realm}}/{{data.char2.name}}
{{/if}}
</a>
</span>
</div>
</div>
{{! Add this section before the categories loop }}
<div class="quests-table" style="margin-bottom: 20px;">
<h2 class="is-size-4 category-header collapsible expanded">
Active Quests
<span class="collapse-icon">▼</span>
</h2>
<div class="category-content expanded">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>Quest</th>
<th>Area (Level)</th>
<th>{{data.char1.name}}</th>
<th>{{data.char2.name}}</th>
</tr>
</thead>
<tbody>
{{#each (getActiveQuests data.categories)}}
<tr class="{{getDiffClass this}}">
<td class="quest-name">
<a href="{{@root.wowhead}}/quest={{this.id}}">{{this.title}}</a>
</td>
<td class="quest-level">{{this.category}} ({{this.questLevel}})</td>
<td class="quest-status {{this.char1Status}}">{{this.char1Status}}</td>
<td class="quest-status {{this.char2Status}}">{{this.char2Status}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
{{#each data.categories as |quests category|}}
<div class="quests-table" style="margin-bottom: 20px;">
<h2 class="is-size-4 category-header collapsible">
{{@key}}
{{#with (getDiffCount this)}}
{{#if this}}
<span class="diff-count">({{this}} different)</span>
{{/if}}
{{/with}}
<span class="collapse-icon">▼</span>
</h2>
<div class="category-content">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>Quest</th>
<th>Level</th>
<th>{{../data.char1.name}}</th>
<th>{{../data.char2.name}}</th>
</tr>
</thead>
<tbody>
{{#each this}}
<tr class="{{getDiffClass this}}">
<td class="quest-name">
<a href="{{@root.wowhead}}/quest={{this.id}}">{{this.title}}</a>
</td>
<td class="quest-level">{{this.questLevel}}</td>
<td class="quest-status {{this.char1Status}}">{{this.char1Status}}</td>
<td class="quest-status {{this.char2Status}}">{{this.char2Status}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
{{/each}}
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.collapsible').forEach(header => {
header.addEventListener('click', function() {
const content = this.nextElementSibling;
content.classList.toggle('expanded');
this.classList.toggle('expanded');
});
});
const compareSelect = document.getElementById('compareCharacter');
const compareButton = document.getElementById('compareButton');
compareSelect.addEventListener('change', function() {
compareButton.disabled = !compareSelect.value;
});
compareButton.addEventListener('click', function() {
const selected = compareSelect.value;
if (selected) {
const [realm, name] = selected.split('/');
// Get the base path by removing everything after /quests/compare/
const basePath = window.location.pathname.split('/quests/compare/')[0];
const safeRealm = encodeURIComponent(realm);
const safeName = encodeURIComponent(name);
window.location.href = `${basePath}/quests/compare/${safeRealm}/${safeName}`;
}
});
});
</script>

141
static/character-quests.hbs Normal file
View file

@ -0,0 +1,141 @@
<link rel="stylesheet" type="text/css" href="{{websiteRoot}}/css/character-quests.css">
{{> icons }}
<script type="application/javascript">
const aowow_tooltips = { "renamelinks": true, };
</script>
{{> character-header }}
<div class="quest-compare-section" style="margin-bottom: 20px;">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Compare with:</label>
</div>
<div class="field-body">
<div class="field has-addons">
<div class="control is-expanded">
<div class="select is-fullwidth">
<select id="compareCharacter">
<option value="">Select a character...</option>
{{#each data.otherCharacters}}
<option value="{{this.realmName}}/{{this.name}}">{{this.name}}</option>
{{/each}}
</select>
</div>
</div>
<div class="control">
<button class="button is-info" id="compareButton" disabled>
Compare Quests
</button>
</div>
</div>
</div>
</div>
</div>
{{#each data.categories as |quests category|}}
<div class="quests-table" style="margin-bottom: 20px;">
{{#if (hasInProgressQuests quests)}}
<h2 class="is-size-4 category-header collapsible expanded">
{{else}}
<h2 class="is-size-4 category-header collapsible">
{{/if}}
{{category}}
<span class="collapse-icon">▼</span>
</h2>
{{#if (hasInProgressQuests quests)}}
<div class="category-content expanded">
{{else}}
<div class="category-content">
{{/if}}
<!-- In Progress Quests -->
<h3 class="is-size-5 collapsible">
In Progress
<span class="collapse-icon">▼</span>
</h3>
<div class="collapsible-content expanded">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>Quest</th>
<th>Level</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{{#each quests}}
{{#if (eq this.status "In Progress")}}
<tr>
<td class="quest-name"><a href="{{@root.wowhead}}/quest={{this.id}}">{{this.title}}</a></td>
<td class="quest-level">{{this.questLevel}}</td>
<td class="quest-status {{this.status}}">{{this.status}}</td>
</tr>
{{/if}}
{{/each}}
</tbody>
</table>
</div>
<!-- Completed Quests -->
<h3 class="is-size-5 collapsible">
Completed
<span class="collapse-icon">▼</span>
</h3>
<div class="collapsible-content">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>Quest</th>
<th>Level</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{{#each quests}}
{{#if (eq this.status "Completed")}}
<tr>
<td class="quest-name"><a href="{{@root.wowhead}}/quest={{this.id}}">{{this.title}}</a></td>
<td class="quest-level">{{this.questLevel}}</td>
<td class="quest-status {{this.status}}">{{this.status}}</td>
</tr>
{{/if}}
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
{{/each}}
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.collapsible').forEach(header => {
header.addEventListener('click', function(e) {
// Stop event from bubbling to parent collapsible elements
e.stopPropagation();
const content = this.nextElementSibling;
content.classList.toggle('expanded');
this.classList.toggle('expanded');
});
});
const compareSelect = document.getElementById('compareCharacter');
const compareButton = document.getElementById('compareButton');
compareSelect.addEventListener('change', function() {
compareButton.disabled = !this.value;
});
compareButton.addEventListener('click', function() {
const selected = compareSelect.value;
if (selected) {
const [realm, name] = selected.split('/');
const sanitizedRealm = encodeURIComponent(realm);
const sanitizedName = encodeURIComponent(name);
window.location.href = `${window.location.pathname}/compare/${sanitizedRealm}/${sanitizedName}`;
}
});
});
</script>

View file

@ -0,0 +1,82 @@
<link rel="stylesheet" type="text/css" href="{{websiteRoot}}/css/character-reputation.css">
{{> icons }}
<script type="application/javascript">
const aowow_tooltips = { "renamelinks": true, };
</script>
{{> character-header }}
<div class="reputation-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Classic Reputations</h2>
<table class="table is-striped is-hoverable is-fullwidth">
<tbody>
{{#each data.classic}}
<tr>
<!-- Faction ID: {{this.id}} -->
<td>
<div class="reputation-row">
<span class="faction-name">{{this.name}}</span>
<div class="progress-section">
<div class="progress-bar-container">
<div class="progress-bar {{this.standing}}" style="width: {{multiply (divide this.valueInGrade this.max) 100}}%;"></div>
<span class="standing-text">{{this.standing}}</span>
<span class="value-text">{{this.valueInGrade}} / {{this.max}}</span>
</div>
</div>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="reputation-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">The Burning Crusade</h2>
<table class="table is-striped is-hoverable is-fullwidth">
<tbody>
{{#each data.tbc}}
<tr>
<!-- Faction ID: {{this.id}} -->
<td>
<div class="reputation-row">
<span class="faction-name">{{this.name}}</span>
<div class="progress-section">
<div class="progress-bar-container">
<div class="progress-bar {{this.standing}}" style="width: {{multiply (divide this.valueInGrade this.max) 100}}%;"></div>
<span class="standing-text">{{this.standing}}</span>
<span class="value-text">{{this.valueInGrade}} / {{this.max}}</span>
</div>
</div>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="reputation-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Wrath of the Lich King</h2>
<table class="table is-striped is-hoverable is-fullwidth">
<tbody>
{{#each data.wotlk}}
<tr>
<!-- Faction ID: {{this.id}} -->
<td>
<div class="reputation-row">
<span class="faction-name">{{this.name}}</span>
<div class="progress-section">
<div class="progress-bar-container">
<div class="progress-bar {{this.standing}}" style="width: {{multiply (divide this.valueInGrade this.max) 100}}%;"></div>
<span class="standing-text">{{this.standing}}</span>
<span class="value-text">{{this.valueInGrade}} / {{this.max}}</span>
</div>
</div>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>

125
static/character-skills.hbs Normal file
View file

@ -0,0 +1,125 @@
<link rel="stylesheet" type="text/css" href="{{websiteRoot}}/css/character-skills.css">
{{> icons }}
<script type="application/javascript">
const aowow_tooltips = { "renamelinks": true, };
</script>
{{> character-header }}
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Professions</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.professions}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Secondary Skills</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.secondarySkills}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Weapon Skills</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.weaponSkills}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Class Skills</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.classSkills}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Armor Proficiencies</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.armorSkills}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class="skills-table" style="margin-bottom: 20px;">
<h2 class="is-size-4">Languages</h2>
<table class="table is-striped is-hoverable is-quarter-desktop">
<tbody>
{{#each data.languages}}
<tr>
<!-- Skill ID: {{this.id}} -->
<td>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {{multiply (divide this.value this.max) 100}}%;"></div>
<span class="progress-text"><span class="gold-text">{{this.skill}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{this.value}}/{{this.max}}</span>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<script type="application/javascript">
// Add any additional JavaScript logic if needed
</script>

View file

@ -0,0 +1,160 @@
.quests-table {
max-width: 500px;
width: 100%;
}
.quests-table h2 {
color: #FFD700;
}
.quest-name {
color: white;
}
.quest-level {
text-align: center;
width: 80px;
}
.quest-status {
text-align: center;
width: 120px;
}
.quest-status.completed {
color: #4CAF50;
}
.quest-status.in-progress {
color: #FFD100;
}
.collapsible {
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 10px;
}
.collapse-icon {
display: inline-block;
transition: transform 0.3s ease;
font-size: 12px;
}
.collapsible.expanded .collapse-icon {
transform: rotate(180deg);
}
.collapsible-content {
display: none;
overflow: hidden;
transition: height 0.3s ease-out;
}
.collapsible-content.expanded {
display: block;
}
.completed-quests.expanded .collapsible-content {
display: block;
}
.completed-quests.expanded .collapse-icon {
transform: rotate(180deg);
}
.quest-category {
margin-bottom: 20px;
}
.quest-category h3 {
color: #ccc;
margin-bottom: 10px;
}
.is-size-5 {
margin-bottom: 10px;
}
.category-header {
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 10px;
}
.category-content {
display: none;
margin-top: 10px;
}
.category-content.expanded {
display: block;
}
.category-header .collapse-icon {
margin-left: auto;
}
.category-header.expanded .collapse-icon {
transform: rotate(180deg);
}
.category-header:not(.expanded) .collapse-icon {
transform: rotate(-90deg);
}
.quest-comparison-header {
margin-bottom: 20px;
text-align: center;
max-width: 500px;
}
.quest-comparison-header .characters {
font-size: 1.2em;
margin-top: 10px;
}
.quest-comparison-header .char1,
.quest-comparison-header .char2 {
font-weight: bold;
}
/* tr.quest-diff {
background-color: rgba(255, 255, 0, 0.2) !important;
} */
tr.quest-missing-1 td:nth-child(3) {
background-color: rgba(255, 0, 0, 0.2) !important;
}
tr.quest-missing-2 td:nth-child(4) {
background-color: rgba(255, 0, 0, 0.2) !important;
}
.quest-compare-section {
max-width: 500px;
}
.quest-compare-section .select select {
min-width: 200px;
}
.diff-count {
font-size: 0.8em;
color: #ffdd57;
margin-left: 10px;
font-weight: normal;
}
.quest-status.In.Progress {
color: #4CAF50;
font-weight: bold;
}
.quest-category {
font-style: italic;
}

View file

@ -0,0 +1,108 @@
.reputation-table {
max-width: 500px;
width: 100%;
}
.reputation-table h2 {
color: #FFD700; /* Gold color for section titles */
}
.reputation-row {
display: flex;
align-items: center;
gap: 20px;
}
.faction-name {
min-width: 150px;
font-weight: bold;
color: white; /* White color for faction names */
}
.progress-section {
flex: 1;
display: flex;
gap: 10px;
}
.progress-bar-container {
flex: 1;
background-color: #020018;
border-radius: 5px;
overflow: hidden;
height: 20px;
position: relative;
}
.progress-bar-container:hover .standing-text {
display: none;
}
.progress-bar-container:hover .value-text {
display: block;
}
.progress-bar {
height: 100%;
position: absolute;
top: 0;
left: 0;
}
/* Reputation colors */
.progress-bar.Neutral {
background-color: #B8A44A; /* More muted yellow */
}
.progress-bar.Friendly,
.progress-bar.Honored,
.progress-bar.Revered,
.progress-bar.Exalted {
background-color: #4CAF50; /* More muted green */
}
.progress-bar.Hostile,
.progress-bar.Hated {
background-color: #D32F2F; /* More muted red */
}
.progress-text {
position: absolute;
top: 50%;
left: 10px;
transform: translateY(-50%);
z-index: 2;
color: white;
font-weight: bold;
font-size: 12px;
white-space: nowrap;
}
.standing-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
color: white;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
font-size: 12px;
font-weight: bold;
}
.value-text {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
color: white;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
font-size: 12px;
font-weight: bold;
}
.gold-text {
color: #FFD700;
}

View file

@ -0,0 +1,121 @@
#talent-template,
#talent-tree-template {
display: none;
}
.iconmedium.empty .icon-border {
background-position: 0px 0px;
}
.iconmedium.empty .icon {
filter: grayscale(100%);
opacity: 0.7;
}
.iconmedium.notfull .icon-border {
background-position: -84px 0px;
}
.iconmedium.full .icon-border {
background-position: -42px 0px;
}
.iconmedium .icon-border {
width: 42px;
height: 42px;
position: absolute;
left: 1px;
top: 1px;
background-repeat: no-repeat;
}
.iconmedium a span {
display: none;
}
.iconmedium::after {
content: attr(rank);
background-color: black;
position: absolute;
top: 30px;
left: 30px;
font-size: 14px;
padding: 0px 2px;
z-index: 4;
}
.iconmedium.empty::after {
display: none;
}
.iconmedium.full::after,
.iconmedium.notfull::after {
border-radius: 4px;
cursor: default;
user-select: none;
pointer-events: none;
}
.iconmedium.notfull::after {
color: #30ec30;
border: 1px solid #30ec30;
}
.iconmedium.full::after {
color: #ffcf00;
border: 1px solid #ffcf00;
}
.skills-table {
max-width: 500px; /* Maximum width for the table */
width: 100%; /* Allows the table to shrink responsively */
}
.skills-table table {
width: 100%; /* Ensures all tables inside .skills-table are the same width */
table-layout: fixed; /* Ensures consistent column widths */
}
.skills-table th,
.skills-table td {
width: 33.33%;
}
.progress-bar-container {
width: 100%;
background-color: #020018;
border-radius: 5px;
overflow: hidden;
height: 20px;
position: relative; /* Enables positioning of child elements */
}
.progress-bar {
height: 100%;
background-color: #000852;
position: absolute; /* Allows the bar to stay behind the text */
width: 0%;
transition: width 0.3s ease;
z-index: 1; /* Ensures the bar is behind the text */
}
.progress-text {
position: absolute; /* Positions the text over the bar */
top: 50%; /* Centers the text vertically */
left: 10px; /* Aligns the text to the left with some padding */
transform: translateY(-50%); /* Vertically centers the text */
z-index: 2; /* Ensures the text is above the bar */
color: white; /* Makes the text visible on the bar */
font-weight: bold;
font-size: 12px;
white-space: nowrap; /* Prevents text from wrapping */
}
#spec-links {
margin-bottom: 32px;
}
.gold-text {
color: #FFD700; /* Gold color */
font-weight: bold; /* Optional: Make the text bold */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,7 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="{{websiteRoot}}/img/w_armory_favicon_32x32.ico">
<link rel="apple-touch-icon" sizes="180x180" href="{{websiteRoot}}/img/w_armory_favicon_180x180.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{websiteRoot}}/img/w_armory_favicon_32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{websiteRoot}}/img/w_armory_favicon_16x16.png">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{title}}</title>

View file

@ -3,6 +3,15 @@
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}">Character</a>&emsp;
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/talents">Talents</a>&emsp;
{{#if enableSkillsPage}}
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/skills">Skills</a>&emsp;
{{/if}}
{{#if enableReputationPage}}
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/reputation">Reputation</a>&emsp;
{{/if}}
{{#if enableQuestsPage}}
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/quests">Quests</a>&emsp;
{{/if}}
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/achievements">Achievements</a>&emsp;
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/pvp">PvP</a>&emsp;
<br>
@ -24,6 +33,11 @@
{{else}}
Offline 🔴
{{/if}}
<a href="{{wowhead}}/zone={{zone}}">
{{#if zoneName}}
{{zoneName}}
{{/if}}
</a>
</div>
<br>