diff --git a/game/gw2_provider.php b/game/gw2_provider.php index 04848f4..9f4971f 100644 --- a/game/gw2_provider.php +++ b/game/gw2_provider.php @@ -144,10 +144,11 @@ public function get_armor_types(): array * meta play, not every possible build — several specs (e.g. Catalyst, * Vindicator) can flex into other roles depending on traits/gear. * - * spec_icon intentionally left empty: no icon assets exist yet for - * these specs. Core handles the empty-icon case already (roster spec - * resolution has dedicated test coverage for it). Tracked separately - * as a follow-up. + * spec_icon names a file in images/spec_icons/ (core resolves it as + * spec_icons/.png, see roster::resolve_spec()). The assets + * are the official elite-specialization icons from the Guild Wars 2 + * API's render service (/v2/specializations), normalised from their + * native 64x64 to the 56x56 used by bbguildwow's spec icons. * * @return array> */ @@ -157,49 +158,49 @@ public static function spec_catalog(): array return array( 1 => array( // Warrior - array('spec_name' => 'Berserker', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Spellbreaker', 'role_id' => $control, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Bladesworn', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Berserker', 'role_id' => $damage, 'spec_icon' => 'warrior_berserker', 'spec_order' => 1), + array('spec_name' => 'Spellbreaker', 'role_id' => $control, 'spec_icon' => 'warrior_spellbreaker', 'spec_order' => 2), + array('spec_name' => 'Bladesworn', 'role_id' => $damage, 'spec_icon' => 'warrior_bladesworn', 'spec_order' => 3), ), 2 => array( // Guardian - array('spec_name' => 'Dragonhunter', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Firebrand', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Willbender', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Dragonhunter', 'role_id' => $damage, 'spec_icon' => 'guardian_dragonhunter', 'spec_order' => 1), + array('spec_name' => 'Firebrand', 'role_id' => $support, 'spec_icon' => 'guardian_firebrand', 'spec_order' => 2), + array('spec_name' => 'Willbender', 'role_id' => $damage, 'spec_icon' => 'guardian_willbender', 'spec_order' => 3), ), 3 => array( // Engineer - array('spec_name' => 'Scrapper', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Holosmith', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Mechanist', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Scrapper', 'role_id' => $support, 'spec_icon' => 'engineer_scrapper', 'spec_order' => 1), + array('spec_name' => 'Holosmith', 'role_id' => $damage, 'spec_icon' => 'engineer_holosmith', 'spec_order' => 2), + array('spec_name' => 'Mechanist', 'role_id' => $support, 'spec_icon' => 'engineer_mechanist', 'spec_order' => 3), ), 4 => array( // Ranger - array('spec_name' => 'Druid', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Soulbeast', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Untamed', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Druid', 'role_id' => $support, 'spec_icon' => 'ranger_druid', 'spec_order' => 1), + array('spec_name' => 'Soulbeast', 'role_id' => $damage, 'spec_icon' => 'ranger_soulbeast', 'spec_order' => 2), + array('spec_name' => 'Untamed', 'role_id' => $damage, 'spec_icon' => 'ranger_untamed', 'spec_order' => 3), ), 5 => array( // Thief - array('spec_name' => 'Daredevil', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Deadeye', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Specter', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Daredevil', 'role_id' => $damage, 'spec_icon' => 'thief_daredevil', 'spec_order' => 1), + array('spec_name' => 'Deadeye', 'role_id' => $damage, 'spec_icon' => 'thief_deadeye', 'spec_order' => 2), + array('spec_name' => 'Specter', 'role_id' => $support, 'spec_icon' => 'thief_specter', 'spec_order' => 3), ), 6 => array( // Elementalist - array('spec_name' => 'Tempest', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Weaver', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Catalyst', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Tempest', 'role_id' => $support, 'spec_icon' => 'elementalist_tempest', 'spec_order' => 1), + array('spec_name' => 'Weaver', 'role_id' => $damage, 'spec_icon' => 'elementalist_weaver', 'spec_order' => 2), + array('spec_name' => 'Catalyst', 'role_id' => $support, 'spec_icon' => 'elementalist_catalyst', 'spec_order' => 3), ), 7 => array( // Mesmer - array('spec_name' => 'Chronomancer', 'role_id' => $control, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Mirage', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Virtuoso', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Chronomancer', 'role_id' => $control, 'spec_icon' => 'mesmer_chronomancer', 'spec_order' => 1), + array('spec_name' => 'Mirage', 'role_id' => $damage, 'spec_icon' => 'mesmer_mirage', 'spec_order' => 2), + array('spec_name' => 'Virtuoso', 'role_id' => $damage, 'spec_icon' => 'mesmer_virtuoso', 'spec_order' => 3), ), 8 => array( // Necromancer - array('spec_name' => 'Reaper', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Scourge', 'role_id' => $control, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Harbinger', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Reaper', 'role_id' => $damage, 'spec_icon' => 'necromancer_reaper', 'spec_order' => 1), + array('spec_name' => 'Scourge', 'role_id' => $control, 'spec_icon' => 'necromancer_scourge', 'spec_order' => 2), + array('spec_name' => 'Harbinger', 'role_id' => $damage, 'spec_icon' => 'necromancer_harbinger', 'spec_order' => 3), ), 9 => array( // Revenant - array('spec_name' => 'Herald', 'role_id' => $support, 'spec_icon' => '', 'spec_order' => 1), - array('spec_name' => 'Renegade', 'role_id' => $control, 'spec_icon' => '', 'spec_order' => 2), - array('spec_name' => 'Vindicator', 'role_id' => $damage, 'spec_icon' => '', 'spec_order' => 3), + array('spec_name' => 'Herald', 'role_id' => $support, 'spec_icon' => 'revenant_herald', 'spec_order' => 1), + array('spec_name' => 'Renegade', 'role_id' => $control, 'spec_icon' => 'revenant_renegade', 'spec_order' => 2), + array('spec_name' => 'Vindicator', 'role_id' => $damage, 'spec_icon' => 'revenant_vindicator', 'spec_order' => 3), ), ); } diff --git a/images/spec_icons/elementalist_catalyst.png b/images/spec_icons/elementalist_catalyst.png new file mode 100644 index 0000000..e9142ef Binary files /dev/null and b/images/spec_icons/elementalist_catalyst.png differ diff --git a/images/spec_icons/elementalist_tempest.png b/images/spec_icons/elementalist_tempest.png new file mode 100644 index 0000000..2496609 Binary files /dev/null and b/images/spec_icons/elementalist_tempest.png differ diff --git a/images/spec_icons/elementalist_weaver.png b/images/spec_icons/elementalist_weaver.png new file mode 100644 index 0000000..4f58559 Binary files /dev/null and b/images/spec_icons/elementalist_weaver.png differ diff --git a/images/spec_icons/engineer_holosmith.png b/images/spec_icons/engineer_holosmith.png new file mode 100644 index 0000000..38962b1 Binary files /dev/null and b/images/spec_icons/engineer_holosmith.png differ diff --git a/images/spec_icons/engineer_mechanist.png b/images/spec_icons/engineer_mechanist.png new file mode 100644 index 0000000..165d967 Binary files /dev/null and b/images/spec_icons/engineer_mechanist.png differ diff --git a/images/spec_icons/engineer_scrapper.png b/images/spec_icons/engineer_scrapper.png new file mode 100644 index 0000000..e36ab30 Binary files /dev/null and b/images/spec_icons/engineer_scrapper.png differ diff --git a/images/spec_icons/guardian_dragonhunter.png b/images/spec_icons/guardian_dragonhunter.png new file mode 100644 index 0000000..554a041 Binary files /dev/null and b/images/spec_icons/guardian_dragonhunter.png differ diff --git a/images/spec_icons/guardian_firebrand.png b/images/spec_icons/guardian_firebrand.png new file mode 100644 index 0000000..e6c83aa Binary files /dev/null and b/images/spec_icons/guardian_firebrand.png differ diff --git a/images/spec_icons/guardian_willbender.png b/images/spec_icons/guardian_willbender.png new file mode 100644 index 0000000..0801a99 Binary files /dev/null and b/images/spec_icons/guardian_willbender.png differ diff --git a/images/spec_icons/mesmer_chronomancer.png b/images/spec_icons/mesmer_chronomancer.png new file mode 100644 index 0000000..18a155c Binary files /dev/null and b/images/spec_icons/mesmer_chronomancer.png differ diff --git a/images/spec_icons/mesmer_mirage.png b/images/spec_icons/mesmer_mirage.png new file mode 100644 index 0000000..616bb78 Binary files /dev/null and b/images/spec_icons/mesmer_mirage.png differ diff --git a/images/spec_icons/mesmer_virtuoso.png b/images/spec_icons/mesmer_virtuoso.png new file mode 100644 index 0000000..c0a58fa Binary files /dev/null and b/images/spec_icons/mesmer_virtuoso.png differ diff --git a/images/spec_icons/necromancer_harbinger.png b/images/spec_icons/necromancer_harbinger.png new file mode 100644 index 0000000..5b55c9a Binary files /dev/null and b/images/spec_icons/necromancer_harbinger.png differ diff --git a/images/spec_icons/necromancer_reaper.png b/images/spec_icons/necromancer_reaper.png new file mode 100644 index 0000000..f5d8ac4 Binary files /dev/null and b/images/spec_icons/necromancer_reaper.png differ diff --git a/images/spec_icons/necromancer_scourge.png b/images/spec_icons/necromancer_scourge.png new file mode 100644 index 0000000..d9dee3c Binary files /dev/null and b/images/spec_icons/necromancer_scourge.png differ diff --git a/images/spec_icons/ranger_druid.png b/images/spec_icons/ranger_druid.png new file mode 100644 index 0000000..fc17b11 Binary files /dev/null and b/images/spec_icons/ranger_druid.png differ diff --git a/images/spec_icons/ranger_soulbeast.png b/images/spec_icons/ranger_soulbeast.png new file mode 100644 index 0000000..39ffedb Binary files /dev/null and b/images/spec_icons/ranger_soulbeast.png differ diff --git a/images/spec_icons/ranger_untamed.png b/images/spec_icons/ranger_untamed.png new file mode 100644 index 0000000..4fa3352 Binary files /dev/null and b/images/spec_icons/ranger_untamed.png differ diff --git a/images/spec_icons/revenant_herald.png b/images/spec_icons/revenant_herald.png new file mode 100644 index 0000000..0c52be7 Binary files /dev/null and b/images/spec_icons/revenant_herald.png differ diff --git a/images/spec_icons/revenant_renegade.png b/images/spec_icons/revenant_renegade.png new file mode 100644 index 0000000..3b0c52b Binary files /dev/null and b/images/spec_icons/revenant_renegade.png differ diff --git a/images/spec_icons/revenant_vindicator.png b/images/spec_icons/revenant_vindicator.png new file mode 100644 index 0000000..8f77f62 Binary files /dev/null and b/images/spec_icons/revenant_vindicator.png differ diff --git a/images/spec_icons/thief_daredevil.png b/images/spec_icons/thief_daredevil.png new file mode 100644 index 0000000..3e35dc4 Binary files /dev/null and b/images/spec_icons/thief_daredevil.png differ diff --git a/images/spec_icons/thief_deadeye.png b/images/spec_icons/thief_deadeye.png new file mode 100644 index 0000000..73906fc Binary files /dev/null and b/images/spec_icons/thief_deadeye.png differ diff --git a/images/spec_icons/thief_specter.png b/images/spec_icons/thief_specter.png new file mode 100644 index 0000000..54effdb Binary files /dev/null and b/images/spec_icons/thief_specter.png differ diff --git a/images/spec_icons/warrior_berserker.png b/images/spec_icons/warrior_berserker.png new file mode 100644 index 0000000..84d5f33 Binary files /dev/null and b/images/spec_icons/warrior_berserker.png differ diff --git a/images/spec_icons/warrior_bladesworn.png b/images/spec_icons/warrior_bladesworn.png new file mode 100644 index 0000000..cf5a81b Binary files /dev/null and b/images/spec_icons/warrior_bladesworn.png differ diff --git a/images/spec_icons/warrior_spellbreaker.png b/images/spec_icons/warrior_spellbreaker.png new file mode 100644 index 0000000..3e5ce26 Binary files /dev/null and b/images/spec_icons/warrior_spellbreaker.png differ diff --git a/tests/game/gw2_provider_test.php b/tests/game/gw2_provider_test.php index 0de2820..d332658 100644 --- a/tests/game/gw2_provider_test.php +++ b/tests/game/gw2_provider_test.php @@ -75,6 +75,34 @@ public function test_api_locales_empty(): void $this->assertEmpty($this->provider->get_api_locales()); } + public function test_every_elite_spec_has_an_icon_asset(): void + { + $missing_value = array(); + $missing_file = array(); + + foreach (gw2_provider::spec_catalog() as $class_id => $specs) + { + foreach ($specs as $spec) + { + if ($spec['spec_icon'] === '') + { + $missing_value[] = $spec['spec_name']; + continue; + } + + $file = dirname(__DIR__, 2) . '/images/spec_icons/' . $spec['spec_icon'] . '.png'; + + if (!file_exists($file)) + { + $missing_file[] = $spec['spec_name'] . ' => ' . $spec['spec_icon'] . '.png'; + } + } + } + + $this->assertSame(array(), $missing_value, 'Specs with an empty spec_icon: ' . implode(', ', $missing_value)); + $this->assertSame(array(), $missing_file, 'Specs whose icon file is missing: ' . implode(', ', $missing_file)); + } + public function test_armor_types(): void { $armor = $this->provider->get_armor_types();