Merge f03171023a into 6cb84817ac
|
|
@ -1,4 +1,5 @@
|
||||||
ACORE_ARMORY_AOWOW_URL="https://wowgaming.altervista.org/aowow"
|
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_URL="https://mywebsite.com"
|
||||||
ACORE_ARMORY_WEBSITE_NAME="My Website"
|
ACORE_ARMORY_WEBSITE_NAME="My Website"
|
||||||
ACORE_ARMORY_WEBSITE_ROOT=""
|
ACORE_ARMORY_WEBSITE_ROOT=""
|
||||||
|
|
@ -22,3 +23,6 @@ ACORE_ARMORY_WORLD_DATABASE__USER="acore"
|
||||||
ACORE_ARMORY_WORLD_DATABASE__PASSWORD="acore"
|
ACORE_ARMORY_WORLD_DATABASE__PASSWORD="acore"
|
||||||
ACORE_ARMORY_WORLD_DATABASE__DATABASE="acore_world"
|
ACORE_ARMORY_WORLD_DATABASE__DATABASE="acore_world"
|
||||||
ACORE_ARMORY_DB_QUERY_TIMEOUT=10000
|
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>
|
</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 |
|
| 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 |
|
| `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 |
|
| `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 |
|
| `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"` |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
@ -259,8 +267,11 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
|
||||||
- [X] Achievements
|
- [X] Achievements
|
||||||
- [X] PvP statistics, including arena teams
|
- [X] PvP statistics, including arena teams
|
||||||
- [ ] Statistics (from the achievements panel in-game)
|
- [ ] 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)
|
- [ ] Stats (from the character sheet, i.e. health, mana, etc)
|
||||||
|
- [X] Current Zone
|
||||||
- [X] Guild page
|
- [X] Guild page
|
||||||
- [X] Guild emblem
|
- [X] Guild emblem
|
||||||
- [X] Members list
|
- [X] Members list
|
||||||
|
|
@ -270,6 +281,7 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
|
||||||
- [ ] PvP ladder
|
- [ ] PvP ladder
|
||||||
- [X] Arena ladder
|
- [X] Arena ladder
|
||||||
- [ ] Achievements 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.
|
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",
|
"aowowUrl": "https://wowgaming.altervista.org/aowow",
|
||||||
|
"wowheadUrl": "https://www.wowhead.com/wotlk",
|
||||||
"websiteUrl": "https://mywebsite.com",
|
"websiteUrl": "https://mywebsite.com",
|
||||||
"websiteName": "My Website",
|
"websiteName": "My Website",
|
||||||
"websiteRoot": "",
|
"websiteRoot": "",
|
||||||
|
|
@ -32,5 +33,10 @@
|
||||||
"password": "acore",
|
"password": "acore",
|
||||||
"database": "acore_world"
|
"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 { CharacterController } from "./controllers/CharacterController";
|
||||||
import { GuildController } from "./controllers/GuildController";
|
import { GuildController } from "./controllers/GuildController";
|
||||||
import { ArenaController } from "./controllers/ArenaController";
|
import { ArenaController } from "./controllers/ArenaController";
|
||||||
|
import { IQuest, IQuestComparison } from './types/QuestTypes';
|
||||||
|
|
||||||
export class Armory {
|
export class Armory {
|
||||||
public characterCustomization: CharacterCustomization;
|
public characterCustomization: CharacterCustomization;
|
||||||
|
|
@ -81,6 +82,7 @@ export class Armory {
|
||||||
|
|
||||||
const locals = {
|
const locals = {
|
||||||
aowow: this.config.aowowUrl,
|
aowow: this.config.aowowUrl,
|
||||||
|
wowhead: this.config.wowheadUrl,
|
||||||
websiteUrl: this.config.websiteUrl,
|
websiteUrl: this.config.websiteUrl,
|
||||||
websiteName: this.config.websiteName,
|
websiteName: this.config.websiteName,
|
||||||
websiteRoot: this.config.websiteRoot,
|
websiteRoot: this.config.websiteRoot,
|
||||||
|
|
@ -101,6 +103,45 @@ export class Armory {
|
||||||
helpers: {
|
helpers: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
...require("handlebars-helpers")(),
|
...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();
|
await charsController.load();
|
||||||
app.get("/character/:realm/:name", this.wrapRoute(charsController.character.bind(charsController)));
|
app.get("/character/:realm/:name", this.wrapRoute(charsController.character.bind(charsController)));
|
||||||
app.get("/character/:realm/:name/talents", this.wrapRoute(charsController.talents.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/: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/:character/achievements/data", this.wrapRoute(charsController.achievementsData.bind(charsController)));
|
||||||
app.get("/character/:realm/:name/pvp", this.wrapRoute(charsController.pvp.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);
|
const guildsController = new GuildController(this);
|
||||||
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));
|
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,15 @@ export interface IIframeModeConfig {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFeatureFlags {
|
||||||
|
enableQuestsPage: boolean;
|
||||||
|
enableReputationPage: boolean;
|
||||||
|
enableSkillsPage: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
public aowowUrl: string;
|
public aowowUrl: string;
|
||||||
|
public wowheadUrl: string;
|
||||||
public websiteUrl: string;
|
public websiteUrl: string;
|
||||||
public websiteName: string;
|
public websiteName: string;
|
||||||
public websiteRoot: string;
|
public websiteRoot: string;
|
||||||
|
|
@ -36,6 +43,7 @@ export class Config {
|
||||||
public realms: IRealmConfig[];
|
public realms: IRealmConfig[];
|
||||||
public worldDatabase: IDatabaseConfig;
|
public worldDatabase: IDatabaseConfig;
|
||||||
public dbQueryTimeout: number;
|
public dbQueryTimeout: number;
|
||||||
|
public featureFlags: IFeatureFlags;
|
||||||
|
|
||||||
private static envPrefix = "ACORE_ARMORY";
|
private static envPrefix = "ACORE_ARMORY";
|
||||||
private static checkedMissingField = false;
|
private static checkedMissingField = false;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { RowDataPacket } from "mysql2/promise";
|
||||||
import { Armory } from "../Armory";
|
import { Armory } from "../Armory";
|
||||||
import { IRealmConfig } from "../Config";
|
import { IRealmConfig } from "../Config";
|
||||||
import { IEmblem, Utils } from "../Utils";
|
import { IEmblem, Utils } from "../Utils";
|
||||||
import { IAchievement as IAchievementDbc } from "../data/DbcReader";
|
import { IAchievement as IAchievementDbc, ISkillDbc, IAreas } from "../data/DbcReader";
|
||||||
|
|
||||||
interface ICharacterData {
|
interface ICharacterData {
|
||||||
guid: number;
|
guid: number;
|
||||||
|
|
@ -21,6 +21,11 @@ interface ICharacterData {
|
||||||
playerFlags: number;
|
playerFlags: number;
|
||||||
online: number;
|
online: number;
|
||||||
guild: string;
|
guild: string;
|
||||||
|
map: number;
|
||||||
|
zone: number;
|
||||||
|
zoneName: string;
|
||||||
|
position_x: number;
|
||||||
|
position_y: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IEquipmentData {
|
interface IEquipmentData {
|
||||||
|
|
@ -72,6 +77,33 @@ interface IArenaTeam {
|
||||||
emblem?: IEmblem;
|
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 ItemClassGem = 3;
|
||||||
const SpellMechanicMounted = 21;
|
const SpellMechanicMounted = 21;
|
||||||
const RaceDisplayName = {
|
const RaceDisplayName = {
|
||||||
|
|
@ -101,6 +133,7 @@ const ClassDisplayName = {
|
||||||
|
|
||||||
export class CharacterController {
|
export class CharacterController {
|
||||||
private armory: Armory;
|
private armory: Armory;
|
||||||
|
private areaById: { [key: number]: IAreas };
|
||||||
private itemInventoryTypes: { [key: number]: number };
|
private itemInventoryTypes: { [key: number]: number };
|
||||||
private itemIcons: { [key: number]: number };
|
private itemIcons: { [key: number]: number };
|
||||||
private gemItems: { [key: number]: boolean };
|
private gemItems: { [key: number]: boolean };
|
||||||
|
|
@ -108,6 +141,7 @@ export class CharacterController {
|
||||||
private itemSocketBonuses: { [key: number]: number };
|
private itemSocketBonuses: { [key: number]: number };
|
||||||
private mountSpells: number[];
|
private mountSpells: number[];
|
||||||
private mountBySpellId: { [key: number]: IMount };
|
private mountBySpellId: { [key: number]: IMount };
|
||||||
|
private skillById: { [key: number]: ISkillDbc };
|
||||||
private achievementById: { [key: number]: IAchievementDbc };
|
private achievementById: { [key: number]: IAchievementDbc };
|
||||||
|
|
||||||
public constructor(armory: Armory) {
|
public constructor(armory: Armory) {
|
||||||
|
|
@ -180,6 +214,15 @@ export class CharacterController {
|
||||||
for await (const achievement of this.armory.dbc.achievement()) {
|
for await (const achievement of this.armory.dbc.achievement()) {
|
||||||
this.achievementById[achievement.id] = 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> {
|
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> {
|
public async achievements(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
||||||
const realmName = req.params.realm;
|
const realmName = req.params.realm;
|
||||||
const charName = req.params.name;
|
const charName = req.params.name;
|
||||||
|
|
@ -337,6 +787,11 @@ export class CharacterController {
|
||||||
level: charData.level,
|
level: charData.level,
|
||||||
online: charData.online === 1,
|
online: charData.online === 1,
|
||||||
guild: charData.guild,
|
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 where = typeof character === "string" ? "LOWER(`characters`.`name`) = LOWER(?)" : "`characters`.`guid` = ?";
|
||||||
const [rows] = await this.armory.getCharactersDb(realm.name).query({
|
const [rows] = await this.armory.getCharactersDb(realm.name).query({
|
||||||
sql: `
|
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\`
|
FROM \`characters\`
|
||||||
LEFT JOIN \`guild_member\` ON \`guild_member\`.\`guid\` = \`characters\`.\`guid\`
|
LEFT JOIN \`guild_member\` ON \`guild_member\`.\`guid\` = \`characters\`.\`guid\`
|
||||||
LEFT JOIN \`guild\` ON \`guild\`.\`guildid\` = \`guild_member\`.\`guildid\`
|
LEFT JOIN \`guild\` ON \`guild\`.\`guildid\` = \`guild_member\`.\`guildid\`
|
||||||
|
|
@ -968,6 +1423,31 @@ export class CharacterController {
|
||||||
return talents;
|
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) {
|
private async getTalentTrees(classId: number) {
|
||||||
const items = await this.armory.dbc
|
const items = await this.armory.dbc
|
||||||
.talentTab()
|
.talentTab()
|
||||||
|
|
@ -1103,4 +1583,17 @@ export class CharacterController {
|
||||||
return row;
|
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;
|
mountId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISkillDbc {
|
||||||
|
id: number;
|
||||||
|
categoryId: number;
|
||||||
|
skillCostId: number;
|
||||||
|
name: string;
|
||||||
|
spellIcon: number;
|
||||||
|
altVerb: string;
|
||||||
|
canLink: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ISpellDbc {
|
export interface ISpellDbc {
|
||||||
id: number;
|
id: number;
|
||||||
mechanic: number;
|
mechanic: number;
|
||||||
|
|
@ -102,6 +112,19 @@ export interface ITalentTab {
|
||||||
classMask: number;
|
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> {
|
interface IAsyncGeneratorWithArrayMethods<T> {
|
||||||
[Symbol.asyncIterator](): AsyncGenerator<T>;
|
[Symbol.asyncIterator](): AsyncGenerator<T>;
|
||||||
toArray(): Promise<T[]>;
|
toArray(): Promise<T[]>;
|
||||||
|
|
@ -305,6 +328,8 @@ const dir = path.join(process.cwd(), "data");
|
||||||
export const DbcFiles = {
|
export const DbcFiles = {
|
||||||
achievement: path.join(dir, "Achievement_3.3.5_12340.csv"),
|
achievement: path.join(dir, "Achievement_3.3.5_12340.csv"),
|
||||||
achievementCategory: path.join(dir, "AchievementCategory_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"),
|
glyphProperties: path.join(dir, "GlyphProperties_3.3.5_12340.csv"),
|
||||||
item: path.join(dir, "Item_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"),
|
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"),
|
itemDisplayInfo: path.join(dir, "ItemDisplayInfo_3.3.5_12340.csv"),
|
||||||
mount: path.join(dir, "Mount_9.2.0_41462.csv"),
|
mount: path.join(dir, "Mount_9.2.0_41462.csv"),
|
||||||
mountDisplay: path.join(dir, "MountXDisplay_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"),
|
spell: path.join(dir, "Spell_3.3.5_12340.csv"),
|
||||||
spellItemEnchantment: path.join(dir, "SpellItemEnchantment_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"),
|
spellIcon: path.join(dir, "SpellIcon_3.3.5_12340.csv"),
|
||||||
|
|
@ -323,6 +349,7 @@ export const DbcFiles = {
|
||||||
const dbcFields = {
|
const dbcFields = {
|
||||||
achievement: ["id", "faction", "titleLang0", "descriptionLang0", "category", "points", "flags", "iconId"],
|
achievement: ["id", "faction", "titleLang0", "descriptionLang0", "category", "points", "flags", "iconId"],
|
||||||
achievementCategory: ["id", "parent", "nameLang0"],
|
achievementCategory: ["id", "parent", "nameLang0"],
|
||||||
|
areas: ["id", "zoneName", "mapId", "areaId"],
|
||||||
glyphProperties: ["id", "spellId"],
|
glyphProperties: ["id", "spellId"],
|
||||||
item: ["id", "classId", "subclassId", "displayInfoId", "inventoryType"],
|
item: ["id", "classId", "subclassId", "displayInfoId", "inventoryType"],
|
||||||
itemRetail: ["id", "inventoryType"],
|
itemRetail: ["id", "inventoryType"],
|
||||||
|
|
@ -331,6 +358,7 @@ const dbcFields = {
|
||||||
itemDisplayInfo: ["id", "inventoryIcon0"],
|
itemDisplayInfo: ["id", "inventoryIcon0"],
|
||||||
mount: ["id", "sourceSpellId"],
|
mount: ["id", "sourceSpellId"],
|
||||||
mountDisplay: ["id", "creatureDisplayInfoId", "mountId"],
|
mountDisplay: ["id", "creatureDisplayInfoId", "mountId"],
|
||||||
|
skill: ["id", "name"],
|
||||||
spell: ["id", "mechanic", "spellIconId"],
|
spell: ["id", "mechanic", "spellIconId"],
|
||||||
spellItemEnchantment: ["id", "srcItemId"],
|
spellItemEnchantment: ["id", "srcItemId"],
|
||||||
spellIcon: ["id", "textureFilename"],
|
spellIcon: ["id", "textureFilename"],
|
||||||
|
|
@ -348,12 +376,15 @@ const dbcFields = {
|
||||||
"prereqRank0",
|
"prereqRank0",
|
||||||
],
|
],
|
||||||
talentTab: ["id", "nameLang0", "spellIconId", "classMask"],
|
talentTab: ["id", "nameLang0", "spellIconId", "classMask"],
|
||||||
|
faction: ["id", "reputationId", "name"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export class DbcManager {
|
export class DbcManager {
|
||||||
private _achievement: IAchievement[];
|
private _achievement: IAchievement[];
|
||||||
private _achievementCategory: IAchievementCategory[];
|
private _achievementCategory: IAchievementCategory[];
|
||||||
|
private _areas: IAreas[];
|
||||||
private _glyphProperties: IGlyphProperties[];
|
private _glyphProperties: IGlyphProperties[];
|
||||||
|
private _faction: IFactionDbc[];
|
||||||
private _item: IItemDbc[];
|
private _item: IItemDbc[];
|
||||||
private _itemRetail: IItemRetailDbc[];
|
private _itemRetail: IItemRetailDbc[];
|
||||||
private _itemAppearance: IItemAppearanceDbc[];
|
private _itemAppearance: IItemAppearanceDbc[];
|
||||||
|
|
@ -361,6 +392,7 @@ export class DbcManager {
|
||||||
private _itemDisplayInfo: IItemDisplayInfoDbc[];
|
private _itemDisplayInfo: IItemDisplayInfoDbc[];
|
||||||
private _mount: IMountDbc[];
|
private _mount: IMountDbc[];
|
||||||
private _mountDisplay: IMountXDisplayDbc[];
|
private _mountDisplay: IMountXDisplayDbc[];
|
||||||
|
private _skill: ISkillDbc[];
|
||||||
private _spell: ISpellDbc[];
|
private _spell: ISpellDbc[];
|
||||||
private _spellItemEnchantment: ISpellItemEnchantmentDbc[];
|
private _spellItemEnchantment: ISpellItemEnchantmentDbc[];
|
||||||
private _spellIcon: ISpellIcon[];
|
private _spellIcon: ISpellIcon[];
|
||||||
|
|
@ -373,6 +405,7 @@ export class DbcManager {
|
||||||
DbcFiles.achievementCategory,
|
DbcFiles.achievementCategory,
|
||||||
dbcFields.achievementCategory,
|
dbcFields.achievementCategory,
|
||||||
).toArray();
|
).toArray();
|
||||||
|
this._areas = await this.read<IAreas>(DbcFiles.areas).toArray();
|
||||||
this._glyphProperties = await this.read<IGlyphProperties>(DbcFiles.glyphProperties, dbcFields.glyphProperties).toArray();
|
this._glyphProperties = await this.read<IGlyphProperties>(DbcFiles.glyphProperties, dbcFields.glyphProperties).toArray();
|
||||||
this._item = await this.read<IItemDbc>(DbcFiles.item, dbcFields.item).toArray();
|
this._item = await this.read<IItemDbc>(DbcFiles.item, dbcFields.item).toArray();
|
||||||
this._itemRetail = await this.read<IItemRetailDbc>(DbcFiles.itemRetail, dbcFields.itemRetail).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._itemDisplayInfo = await this.read<IItemDisplayInfoDbc>(DbcFiles.itemDisplayInfo, dbcFields.itemDisplayInfo).toArray();
|
||||||
this._mount = await this.read<IMountDbc>(DbcFiles.mount, dbcFields.mount).toArray();
|
this._mount = await this.read<IMountDbc>(DbcFiles.mount, dbcFields.mount).toArray();
|
||||||
this._mountDisplay = await this.read<IMountXDisplayDbc>(DbcFiles.mountDisplay, dbcFields.mountDisplay).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._spell = await this.read<ISpellDbc>(DbcFiles.spell, dbcFields.spell).toArray();
|
||||||
this._spellItemEnchantment = await this.read<ISpellItemEnchantmentDbc>(
|
this._spellItemEnchantment = await this.read<ISpellItemEnchantmentDbc>(
|
||||||
DbcFiles.spellItemEnchantment,
|
DbcFiles.spellItemEnchantment,
|
||||||
|
|
@ -402,6 +436,14 @@ export class DbcManager {
|
||||||
return this.getLoadedDataOrRead(DbcFiles.achievementCategory, this._achievementCategory, dbcFields.achievementCategory);
|
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() {
|
public glyphProperties() {
|
||||||
return this.getLoadedDataOrRead(DbcFiles.glyphProperties, this._glyphProperties, dbcFields.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);
|
return this.getLoadedDataOrRead(DbcFiles.mountDisplay, this._mountDisplay, dbcFields.mountDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public skill() {
|
||||||
|
return this.getLoadedDataOrRead(DbcFiles.skill, this._skill, dbcFields.skill);
|
||||||
|
}
|
||||||
|
|
||||||
public spell() {
|
public spell() {
|
||||||
return this.getLoadedDataOrRead(DbcFiles.spell, this._spell, dbcFields.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>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<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 charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,15 @@
|
||||||
|
|
||||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}">Character</a> 
|
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}">Character</a> 
|
||||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/talents">Talents</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}}/achievements">Achievements</a> 
|
||||||
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/pvp">PvP</a> 
|
<a href="{{websiteRoot}}/character/{{realm}}/{{name}}/pvp">PvP</a> 
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -20,10 +29,15 @@
|
||||||
<div class="char-realm">{{realm}}</div>
|
<div class="char-realm">{{realm}}</div>
|
||||||
<div>
|
<div>
|
||||||
{{#if online}}
|
{{#if online}}
|
||||||
Online 🟢
|
Online 🟢
|
||||||
{{else}}
|
{{else}}
|
||||||
Offline 🔴
|
Offline 🔴
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<a href="{{wowhead}}/zone={{zone}}">
|
||||||
|
{{#if zoneName}}
|
||||||
|
{{zoneName}}
|
||||||
|
{{/if}}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
|
||||||