Merge f03171023a into 6cb84817ac
|
|
@ -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
|
||||
14
README.md
|
|
@ -73,6 +73,10 @@ I also noticed that such a tool was frequently requested in the AzerothCore Disc
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
</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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
402
data/Factions.csv
Normal 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"
|
||||
|
151
data/Skills.csv
Normal 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
|
||||
|
BIN
readme/img/armory-questcompare.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
readme/img/armory-quests.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
readme/img/armory-rep.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
readme/img/armory-skills.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
17
src/armory/types/QuestTypes.ts
Normal 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;
|
||||
}
|
||||
167
static/character-quests-compare.hbs
Normal 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
|
|
@ -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>
|
||||
82
static/character-reputation.hbs
Normal 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
|
|
@ -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> {{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> {{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> {{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> {{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> {{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> {{this.value}}/{{this.max}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="application/javascript">
|
||||
// Add any additional JavaScript logic if needed
|
||||
</script>
|
||||
160
static/css/character-quests.css
Normal 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;
|
||||
}
|
||||
108
static/css/character-reputation.css
Normal 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;
|
||||
}
|
||||
121
static/css/character-skills.css
Normal 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 */
|
||||
}
|
||||
BIN
static/img/w_armory_favicon_16x16.png
Normal file
|
After Width: | Height: | Size: 615 B |
BIN
static/img/w_armory_favicon_180x180.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
static/img/w_armory_favicon_32x32.ico
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
static/img/w_armory_favicon_32x32.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@
|
|||
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}">Character</a> 
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/talents">Talents</a> 
|
||||
{{#if enableSkillsPage}}
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/skills">Skills</a> 
|
||||
{{/if}}
|
||||
{{#if enableReputationPage}}
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/reputation">Reputation</a> 
|
||||
{{/if}}
|
||||
{{#if enableQuestsPage}}
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/quests">Quests</a> 
|
||||
{{/if}}
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/achievements">Achievements</a> 
|
||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/pvp">PvP</a> 
|
||||
<br>
|
||||
|
|
@ -24,6 +33,11 @@
|
|||
{{else}}
|
||||
Offline 🔴
|
||||
{{/if}}
|
||||
<a href="{{wowhead}}/zone={{zone}}">
|
||||
{{#if zoneName}}
|
||||
{{zoneName}}
|
||||
{{/if}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
|
|
|||