diff --git a/endpoints/achievement/achievement.php b/endpoints/achievement/achievement.php
new file mode 100644
index 00000000..3d36494b
--- /dev/null
+++ b/endpoints/achievement/achievement.php
@@ -0,0 +1,494 @@
+ 100
+* }
+*/
+
+class AchievementBaseResponse extends TemplateResponse implements ICache
+{
+ use TrDetailPage, TrCache;
+
+ protected int $cacheType = CACHE_TYPE_PAGE;
+
+ protected string $template = 'achievement';
+ protected string $pageName = 'achievement';
+ protected ?int $activeTab = parent::TAB_DATABASE;
+ protected array $breadcrumb = [0, 9];
+
+ public int $type = Type::ACHIEVEMENT;
+ public int $typeId = 0;
+ public int $reqCrtQty = 0;
+ public ?array $mail = null;
+ public string $description = '';
+ public array $criteria = [];
+ public ?array $rewards = null;
+
+ private AchievementList $subject;
+
+ public function __construct(string $id)
+ {
+ parent::__construct($id);
+
+ $this->typeId = intVal($id);
+ $this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
+ }
+
+ protected function generate() : void
+ {
+ $this->subject = new AchievementList(array(['id', $this->typeId]));
+ if ($this->subject->error)
+ $this->generateNotFound(Lang::game('achievement'), Lang::achievement('notFound'));
+
+ $this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS));
+
+ $this->h1 = $this->subject->getField('name', true);
+
+ $this->gPageInfo += array(
+ 'type' => $this->type,
+ 'typeId' => $this->typeId,
+ 'name' => $this->h1
+ );
+
+
+ /*************/
+ /* Menu Path */
+ /*************/
+
+ // create page title and path
+ $curCat = $this->subject->getField('category');
+ $catPath = [];
+ while ($curCat > 0)
+ {
+ $catPath[] = $curCat;
+ $curCat = DB::Aowow()->SelectCell('SELECT `parentCat` FROM ?_achievementcategory WHERE `id` = ?d', $curCat);
+ }
+
+ $this->breadcrumb = array_merge($this->breadcrumb, array_reverse($catPath));
+
+
+ /**************/
+ /* Page Title */
+ /**************/
+
+ array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('achievement')));
+
+
+ /***********/
+ /* Infobox */
+ /***********/
+
+ $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
+
+ // points
+ if ($_ = $this->subject->getField('points'))
+ $infobox[] = Lang::achievement('points').Lang::main('colon').'[achievementpoints='.$_.']';
+
+ // location
+ // todo (low)
+
+ // faction
+ $infobox[] = Lang::main('side') . match ($this->subject->getField('faction'))
+ {
+ SIDE_ALLIANCE => '[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]',
+ SIDE_HORDE => '[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]',
+ default => Lang::game('si', SIDE_BOTH) // 0, 3
+ };
+
+ // icon
+ if ($_ = $this->subject->getField('iconId'))
+ {
+ $infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
+ $this->extendGlobalIds(Type::ICON, $_);
+ }
+
+ if ($infobox)
+ $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
+
+
+ /**********/
+ /* Series */
+ /**********/
+
+ $series = [];
+ if ($c = $this->subject->getField('chainId'))
+ {
+ $chainAcv = new AchievementList(array(['chainId', $c]));
+
+ foreach ($chainAcv->iterate() as $aId => $__)
+ {
+ $pos = $chainAcv->getField('chainPos');
+ if (!isset($series[$pos]))
+ $series[$pos] = [];
+
+ $series[$pos][] = array(
+ 'side' => (int)$chainAcv->getField('faction'),
+ 'typeStr' => Type::getFileString(Type::ACHIEVEMENT),
+ 'typeId' => $aId,
+ 'name' => $chainAcv->getField('name', true)
+ );
+ }
+ }
+
+ if ($series)
+ $this->series = [[array_values($series), null]];
+
+
+ /****************/
+ /* Main Content */
+ /****************/
+
+ $this->headIcons = [$this->subject->getField('iconString')];
+ $this->description = $this->subject->getField('description', true);
+ $this->redButtons = array(
+ BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE),
+ BUTTON_LINKS => array(
+ 'linkColor' => 'ffffff00',
+ 'linkId' => Type::getFileString(Type::ACHIEVEMENT).':'.$this->typeId.':"..UnitGUID("player")..":0:0:0:0:0:0:0:0',
+ 'linkName' => $this->h1,
+ 'type' => $this->type,
+ 'typeId' => $this->typeId
+ )
+ );
+ $this->reqCrtQty = $this->subject->getField('reqCriteriaCount');
+
+ if ($this->createMail())
+ $this->addScript([SC_CSS_FILE, 'css/Book.css']);
+
+ // create rewards
+ $rewItems = $rewTitles = [];
+ if ($foo = $this->subject->getField('rewards'))
+ {
+ if ($itemRewards = array_filter($foo, fn($x) => $x[0] == Type::ITEM))
+ {
+ $bar = new ItemList(array(['i.id', array_column($itemRewards, 1)]));
+ foreach ($bar->iterate() as $id => $__)
+ $rewItems[] = new IconElement(Type::ITEM, $id, $bar->getField('name', true), quality: $bar->getField('quality'));
+ }
+
+ if ($titleRewards = array_filter($foo, fn($x) => $x[0] == Type::TITLE))
+ {
+ $bar = new TitleList(array(['id', array_column($titleRewards, 1)]));
+ foreach ($bar->iterate() as $id => $__)
+ $rewTitles[] = Lang::achievement('titleReward', [$id, trim(str_replace('%s', '', $bar->getField('male', true)))]);
+ }
+ }
+
+ if (($text = $this->subject->getField('reward', true)) || $rewItems || $rewTitles)
+ $this->rewards = [$rewItems, $rewTitles, $text];
+
+ // factionchange-equivalent
+ if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId))
+ {
+ $altAcv = new AchievementList(array(['id', abs($pendant)]));
+ if (!$altAcv->error)
+ {
+ $this->transfer = Lang::achievement('_transfer', array(
+ $altAcv->id,
+ ITEM_QUALITY_NORMAL,
+ $altAcv->getField('iconString'),
+ $altAcv->getField('name', true),
+ $pendant > 0 ? 'alliance' : 'horde',
+ $pendant > 0 ? Lang::game('si', SIDE_ALLIANCE) : Lang::game('si', SIDE_HORDE)
+ ));
+ }
+ }
+
+
+ /*****************/
+ /* Criteria List */
+ /*****************/
+
+ // serverside extra-Data (not sure why ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE is set, let a lone a couple hundred times)
+ if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
+ $crtExtraData = DB::World()->select('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> ?d AND `criteria_id` IN (?a)', ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE, $crtIds);
+ else
+ $crtExtraData = [];
+
+ foreach ($this->subject->getCriteria() as $crt)
+ {
+ // hide hidden criteria for regular users (really do..?)
+ // if (($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN) && !User::isInGroup(U_GROUP_STAFF))
+ // continue;
+
+ // alternative display option
+ $crtName = Util::localizedString($crt, 'name');
+ $killSuffix = null;
+
+ $obj = (int)$crt['value1'];
+ $qty = (int)$crt['value2'];
+
+ switch ($crt['type'])
+ {
+ // link to npc
+ case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
+ $killSuffix = Lang::achievement('slain');
+ case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
+ $crtIcon = new IconElement(Type::NPC, $obj, $crtName ?: CreatureList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: $crtName ? null : $killSuffix);
+ break;
+ // link to area (by map)
+ case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
+ case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
+ case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
+ case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
+ case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
+ $zoneId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?', $obj);
+ $crtIcon = new IconElement(Type::ZONE, $zoneId ?: 0, $crtName ?: ZoneList::getName($zoneId), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to area
+ case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
+ case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
+ $crtIcon = new IconElement(Type::ZONE, $obj, $crtName ?: ZoneList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to skills
+ case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
+ case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
+ case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
+ case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
+ $crtIcon = new IconElement(Type::SKILL, $obj, $crtName ?: SkillList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ $this->extendGlobalIds(Type::SKILL, $obj);
+ break;
+ // link to class
+ case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
+ $crtIcon = new IconElement(Type::CHR_CLASS, $obj, $crtName ?: CharClassList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to race
+ case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
+ $crtIcon = new IconElement(Type::CHR_RACE, $obj, $crtName ?: CharRaceList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to achivement
+ case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
+ $crtIcon = new IconElement(Type::ACHIEVEMENT, $obj, $crtName ?: AchievementList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ $this->extendGlobalIds(Type::ACHIEVEMENT, $obj);
+ break;
+ // link to quest
+ case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
+ $crtIcon = new IconElement(Type::QUEST, $obj, $crtName ?: QuestList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to spell
+ case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
+ case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
+ case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
+ case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
+ case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
+ $crtIcon = new IconElement(Type::SPELL, $obj, $crtName ?: SpellList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ $this->extendGlobalIds(Type::SPELL, $obj);
+ break;
+ // link to item
+ case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
+ case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
+ case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
+ case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
+ $item = new ItemList([['id', $obj]]);
+ $crtIcon = new IconElement(Type::ITEM, $obj, $crtName ?: $item->getField('name', true), quality: $item->getField('quality'), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ $this->extendGlobalData($item->getJSGlobals());
+ break;
+ // link to faction (/w target reputation)
+ case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
+ $crtIcon = new IconElement(Type::FACTION, $obj, $crtName ?: FactionList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: '('.Lang::getReputationLevelForPoints($qty).')');
+ break;
+ // link to GObject
+ case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
+ case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
+ $crtIcon = new IconElement(Type::OBJECT, $obj, $crtName ?: GameObjectList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ // link to emote
+ case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
+ $crtIcon = new IconElement(Type::EMOTE, $obj, $crtName ?: EmoteList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
+ break;
+ default:
+ // Add a gold coin icon if required
+ if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER )
+ $crtIcon = new IconElement(0, 0, '', extraText: Util::formatMoney($qty));
+ else
+ $crtIcon = new IconElement(0, 0, $crtName);
+ break;
+ }
+
+ if (User::isInGroup(U_GROUP_STAFF))
+ $crtIcon->extraText .= ' [CriteriaId: '.$crt['id'].']';
+
+ $extraData = [];
+ foreach ($crtExtraData[$crt['id']] ?? [] as $xType => $xData)
+ {
+ switch ($xType)
+ {
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
+ $extraData[] = CreatureList::makeLink($xData['value1']);
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
+ if ($xData['value1'])
+ $extraData[] = CharClassList::makeLink($xData['value1']);
+
+ if ($xData['value2'])
+ $extraData[] = CharRaceList::makeLink($xData['value2']);
+
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
+ $extraData[] = SpellList::makeLink($xData['value1']);
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
+ $extraData[] = ZoneList::makeLink($xData['value1']);
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT:
+ if ($xData['ScriptName'] && User::isInGroup(U_GROUP_STAFF))
+ $extraData[] = 'Script '.$xData['ScriptName'];
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
+ if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
+ $extraData[] = ''.$we->getField('name', true).'';
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID:
+ if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
+ $extraData[] = ''.$z->getField('name', true).'';
+ break;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
+ $extraData[] = TitleList::makeLink($xData['value1']);
+ break;
+ default:
+ if (User::isInGroup(U_GROUP_STAFF))
+ $extraData[] = 'has extra criteria data';
+ }
+ }
+
+ if ($extraData)
+ $crtIcon->extraText .= '
('.implode(', ', $extraData).')';
+
+ $this->criteria[] = $crtIcon;
+ }
+
+
+ /**************/
+ /* Extra Tabs */
+ /**************/
+
+ $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
+
+ // tab: see also
+ $conditions = array(
+ ['name_loc'.Lang::getLocale()->value, $this->subject->getField('name', true)],
+ ['id', $this->typeId, '!']
+ );
+ $saList = new AchievementList($conditions);
+ if (!$saList->error)
+ {
+ $this->extendGlobalData($saList->getJSGlobals());
+
+ $this->lvTabs->addListviewTab(new Listview(array(
+ 'data' => $saList->getListviewData(),
+ 'id' => 'see-also',
+ 'name' => '$LANG.tab_seealso',
+ 'visibleCols' => ['category']
+ ), AchievementList::$brickFile));
+ }
+
+ // tab: criteria of
+ $refs = DB::Aowow()->SelectCol('SELECT `refAchievementId` FROM ?_achievementcriteria WHERE `type` = ?d AND `value1` = ?d',
+ ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT,
+ $this->typeId
+ );
+
+ if (!empty($refs))
+ {
+ $coList = new AchievementList(array(['id', $refs]));
+ if (!$coList->error)
+ {
+ $this->extendGlobalData($coList->getJSGlobals());
+
+ $this->lvTabs->addListviewTab(new Listview(array(
+ 'data' => $coList->getListviewData(),
+ 'id' => 'criteria-of',
+ 'name' => '$LANG.tab_criteriaof',
+ 'visibleCols' => ['category']
+ ), AchievementList::$brickFile));
+ }
+ }
+
+ // tab: condition for
+ $cnd = new Conditions();
+ $cnd->getByCondition(Type::ACHIEVEMENT, $this->typeId)->prepare();
+ if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
+ {
+ $this->extendGlobalData($cnd->getJsGlobals());
+ $this->lvTabs->addDataTab(...$tab);
+ }
+
+ parent::generate();
+
+ if ($this->subject->getField('flags') & ACHIEVEMENT_FLAG_REALM_FIRST)
+ $this->result->registerDisplayHook('infobox', [self::class, 'infoboxHook']);
+ }
+
+ private function createMail() : bool
+ {
+ if ($_ = $this->subject->getField('mailTemplate'))
+ {
+ $letter = DB::Aowow()->selectRow('SELECT * FROM ?_mails WHERE `id` = ?d', $_);
+ if (!$letter)
+ return false;
+
+ $this->mail = array(
+ 'attachments' => [],
+ 'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
+ 'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true)),
+ 'header' => [$_, null, null]
+ );
+ }
+ else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true)))
+ {
+ $this->mail = array(
+ 'attachments' => [],
+ 'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
+ 'text' => $_,
+ 'header' => [-$this->typeId, null, null]
+ );
+ }
+ else
+ return false;
+
+ if ($senderId = $this->subject->getField('sender'))
+ if ($senderName = CreatureList::getName($senderId))
+ $this->mail['header'][1] = Lang::mail('mailBy', [$senderId, $senderName]);
+
+ return true;
+ }
+
+ /* finalize infobox */
+ public static function infoboxHook(Template\PageTemplate &$pt, ?InfoboxMarkup &$markup) : void
+ {
+ // realm first still available?
+ if (!DB::isConnectable(DB_AUTH))
+ return;
+
+ $avlb = [];
+ foreach (Profiler::getRealms() AS $rId => $rData)
+ if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE `achievement` = ?d', $pt->typeId))
+ $avlb[] = Util::ucWords($rData['name']);
+
+ if (!$avlb)
+ return;
+
+ $addRow = Lang::achievement('rfAvailable').implode(', ', $avlb);
+
+ if (!$markup)
+ $markup = new InfoboxMarkup([$addRow], ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
+ else
+ $markup->addItem($addRow);
+ }
+}
+
+?>
diff --git a/endpoints/achievement/achievement_power.php b/endpoints/achievement/achievement_power.php
new file mode 100644
index 00000000..05e78fc6
--- /dev/null
+++ b/endpoints/achievement/achievement_power.php
@@ -0,0 +1,50 @@
+ ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
+ );
+
+ public function __construct($id)
+ {
+ parent::__construct($id);
+
+ // temp locale
+ if ($this->_get['domain'])
+ Lang::load($this->_get['domain']);
+
+ $this->typeId = intVal($id);
+ }
+
+ protected function generate() : void
+ {
+ $achievement = new AchievementList(array(['id', $this->typeId]));
+ if ($achievement->error)
+ $this->cacheType = CACHE_TYPE_NONE;
+ else
+ $opts = array(
+ 'name' => $achievement->getField('name', true),
+ 'tooltip' => $achievement->renderTooltip(),
+ 'icon' => $achievement->getField('iconString')
+ );
+
+ $this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
+ }
+}
+
+?>
diff --git a/endpoints/achievements/achievements.php b/endpoints/achievements/achievements.php
new file mode 100644
index 00000000..70fe460e
--- /dev/null
+++ b/endpoints/achievements/achievements.php
@@ -0,0 +1,166 @@
+ ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
+ );
+ protected array $validCats = array(
+ 92 => true,
+ 96 => [14861, 14862, 14863],
+ 97 => [14777, 14778, 14779, 14780],
+ 95 => [165, 14801, 14802, 14803, 14804, 14881, 14901, 15003],
+ 168 => [14808, 14805, 14806, 14921, 14922, 14923, 14961, 14962, 15001, 15002, 15041, 15042],
+ 169 => [170, 171, 172],
+ 201 => [14864, 14865, 14866],
+ 155 => [160, 187, 159, 163, 161, 162, 158, 14981, 156, 14941],
+ 81 => true,
+ 1 => array (
+ 130 => [140, 145, 147, 191],
+ 141 => true,
+ 128 => [135, 136, 137],
+ 122 => [123, 124, 125, 126, 127],
+ 133 => true,
+ 14807 => [14821, 14822, 14823, 14963, 15021, 15062],
+ 132 => [178, 173],
+ 134 => true,
+ 131 => true,
+ 21 => [152, 153, 154]
+ )
+ );
+
+ public function __construct(string $pageParam)
+ {
+ $this->getCategoryFromUrl($pageParam);
+
+ parent::__construct($pageParam);
+
+ $this->subCat = $pageParam !== '' ? '='.$pageParam : '';
+ $this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
+ $this->filterError = $this->filter->error;
+ }
+
+ protected function generate() : void
+ {
+ $this->h1 = Util::ucFirst(Lang::game('achievements'));
+
+ $conditions = [];
+ if (!User::isInGroup(U_GROUP_EMPLOYEE))
+ $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
+
+ // include child categories if current category is empty
+ if ($this->category)
+ $conditions[] = ['category', end($this->category)];
+
+ $this->filter->evalCriteria();
+
+ if ($fiCnd = $this->filter->getConditions())
+ $conditions[] = $fiCnd;
+
+ $this->filterError = $this->filter->error; // maybe the evalX() caused something
+
+
+ /*************/
+ /* Menu Path */
+ /*************/
+
+ foreach ($this->category as $cat)
+ $this->breadcrumb[] = $cat;
+
+
+ /**************/
+ /* Page Title */
+ /**************/
+
+ array_unshift($this->title, Util::ucFirst(Lang::game('achievements')));
+ if ($this->category)
+ array_unshift($this->title, Lang::achievement('cat', end($this->category)));
+
+
+ /****************/
+ /* Main Content */
+ /****************/
+
+ // fix modern client achievement category structure: top catg [1:char, 2:statistic, 3:guild]
+ if ($this->category && $this->category[0] != 1)
+ $link = '=1.'.implode('.', $this->category);
+ else if ($this->category)
+ $link = '=2'.(count($this->category) > 1 ? '.'.implode('.', array_slice($this->category, 1)) : '');
+ else
+ $link = '';
+
+ $this->redButtons[BUTTON_WOWHEAD] = true;
+ $this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), $this->pageName, $link);
+
+ if ($fiQuery = $this->filter->buildGETParam())
+ $this->wowheadLink .= '&filter='.$fiQuery;
+
+ $acvList = new AchievementList($conditions, ['calcTotal' => true]);
+ if (!$acvList->getMatches() && $this->category)
+ {
+ // ToDo - we also branch into here if the filter prohibits results. That should be skipped.
+ $conditions = [];
+ if ($fiCnd)
+ $conditions[] = $fiCnd;
+ if ($catList = DB::Aowow()->SelectCol('SELECT `id` FROM ?_achievementcategory WHERE `parentCat` IN (?a) OR `parentCat2` IN (?a) ', end($this->category), end($this->category)))
+ $conditions[] = ['category', $catList];
+
+ $acvList = new AchievementList($conditions, ['calcTotal' => true]);
+ }
+
+ $tabData = [];
+ if (!$acvList->error)
+ {
+ $tabData['data'] = $acvList->getListviewData();
+
+ // fill g_items, g_titles, g_achievements
+ $this->extendGlobalData($acvList->getJSGlobals());
+
+ // if we are have different cats display field
+ if ($acvList->hasDiffFields('category'))
+ $tabData['visibleCols'] = ['category'];
+
+ if ($this->filter->fiExtraCols)
+ $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
+
+ // create note if search limit was exceeded
+ if ($acvList->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
+ {
+ $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT'));
+ $tabData['_truncated'] = 1;
+ }
+ }
+ $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
+
+ $this->lvTabs->addListviewTab(new Listview($tabData, AchievementList::$brickFile));
+
+ parent::generate();
+
+ $this->setOnCacheLoaded([self::class, 'onBeforeDisplay']);
+ }
+
+ public static function onBeforeDisplay()
+ {
+ // sort for dropdown-menus in filter
+ Lang::sort('game', 'si');
+ }
+}
+
+?>
diff --git a/localization/locale_dede.php b/localization/locale_dede.php
index 4244baf7..c6660f49 100644
--- a/localization/locale_dede.php
+++ b/localization/locale_dede.php
@@ -1117,12 +1117,11 @@ $lang = array(
'criteria' => "Kriterien",
'points' => "Punkte",
'series' => "Reihe",
- 'outOf' => "von",
'criteriaType' => "Criterium Typ-Id:",
'itemReward' => "Ihr bekommt",
'titleReward' => 'Euch wird der Titel "%s" verliehen',
'slain' => "getötet",
- 'reqNumCrt' => "Benötigt",
+ 'reqNumCrt' => 'Benötigt %1$d von %2$d',
'rfAvailable' => "Verfügbar auf Realm: ",
'_transfer' => 'Dieser Erfolg wird mit %s vertauscht, wenn Ihr zur %s wechselt.',
'cat' => array(
diff --git a/localization/locale_enus.php b/localization/locale_enus.php
index ad214f03..ee2e6d8c 100644
--- a/localization/locale_enus.php
+++ b/localization/locale_enus.php
@@ -1117,12 +1117,11 @@ $lang = array(
'criteria' => "Criteria",
'points' => "Points",
'series' => "Series",
- 'outOf' => "out of",
'criteriaType' => "Criterium Type ID:",
'itemReward' => "You will receive",
'titleReward' => 'You shall be granted the title "%s"',
'slain' => "slain",
- 'reqNumCrt' => "Requires",
+ 'reqNumCrt' => 'Requires %1$d out of %2$d',
'rfAvailable' => "Available on realm: ",
'_transfer' => 'This achievement will be converted to %s if you transfer to %s.',
'cat' => array(
diff --git a/localization/locale_eses.php b/localization/locale_eses.php
index 660fc84c..90771bdf 100644
--- a/localization/locale_eses.php
+++ b/localization/locale_eses.php
@@ -1117,12 +1117,11 @@ $lang = array(
'criteria' => "Requisitos",
'points' => "Puntos",
'series' => "Serie",
- 'outOf' => "de",
'criteriaType' => "Criterium Type ID:",
'itemReward' => "Recibirás",
'titleReward' => 'Deberías obtener el título "%s"',
'slain' => "matado",
- 'reqNumCrt' => "Requiere",
+ 'reqNumCrt' => 'Requiere %1$d de %2$d',
'rfAvailable' => "Disponible en reino: ",
'_transfer' => 'Este logro será convertido a %s si lo transfieres a la %s.',
'cat' => array(
diff --git a/localization/locale_frfr.php b/localization/locale_frfr.php
index 6cc43e08..d1024787 100644
--- a/localization/locale_frfr.php
+++ b/localization/locale_frfr.php
@@ -1117,12 +1117,11 @@ $lang = array(
'criteria' => "Critères",
'points' => "Points",
'series' => "Série",
- 'outOf' => "sur",
'criteriaType' => "Criterium Type ID : ",
'itemReward' => "Vous recevrez",
'titleReward' => "Vous devriez recevoir le titre \"%s\"",
'slain' => "tué",
- 'reqNumCrt' => "Nécessite",
+ 'reqNumCrt' => 'Nécessite %1$d sur %2$d',
'rfAvailable' => "Disponibles sur les royaumes : ",
'_transfer' => 'Cet haut fait sera converti en %s si vous transférez en %s.',
'cat' => array(
diff --git a/localization/locale_ruru.php b/localization/locale_ruru.php
index 79f6c974..a5cc97be 100644
--- a/localization/locale_ruru.php
+++ b/localization/locale_ruru.php
@@ -1117,12 +1117,11 @@ $lang = array(
'criteria' => "Критерий",
'points' => "Очки",
'series' => "Серии",
- 'outOf' => "из",
'criteriaType' => "[Criterium Type ID]:",
'itemReward' => "Вы получите",
'titleReward' => 'Наградное звание: "%s"',
'slain' => "убито",
- 'reqNumCrt' => "Требуется",
+ 'reqNumCrt' => 'Требуется %1$d из %2$d',
'rfAvailable' => "[Available on realm]: ",
'_transfer' => 'Этот предмет превратится в %s, если вы перейдете за %s.',
'cat' => array(
diff --git a/localization/locale_zhcn.php b/localization/locale_zhcn.php
index 666689dd..4fd86896 100644
--- a/localization/locale_zhcn.php
+++ b/localization/locale_zhcn.php
@@ -1116,12 +1116,11 @@ $lang = array(
'criteria' => "达成条件",
'points' => "点数",
'series' => "系列",
- 'outOf' => "/",
'criteriaType' => "条件类型ID:",
'itemReward' => "你将得到",
'titleReward' => '你将被授予头衔"%s"',
'slain' => "杀死",
- 'reqNumCrt' => "要求",
+ 'reqNumCrt' => '要求 %1$d/%2$d',
'rfAvailable' => "在服务器上可用:",
'_transfer' => '这个成就将被转换到%s,如果你转移到%s。',
'cat' => array(
diff --git a/pages/achievement.php b/pages/achievement.php
deleted file mode 100644
index 58010387..00000000
--- a/pages/achievement.php
+++ /dev/null
@@ -1,599 +0,0 @@
- 100
-* }
-*/
-
-// menuId 9: Achievement g_initPath()
-// tabId 0: Database g_initHeader()
-class AchievementPage extends GenericPage
-{
- use TrDetailPage;
-
- protected $mail = [];
- protected $series = null;
- protected $description = '';
- protected $criteria = [];
- protected $rewards = [];
-
- protected $type = Type::ACHIEVEMENT;
- protected $typeId = 0;
- protected $tpl = 'achievement';
- protected $path = [0, 9];
- protected $tabId = 0;
- protected $mode = CACHE_TYPE_PAGE;
-
- protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\Locale::tryFromDomain']];
-
- private $powerTpl = '$WowheadPower.registerAchievement(%d, %d, %s);';
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Lang::load($this->_get['domain']);
-
- $this->typeId = intVal($id);
-
- $this->subject = new AchievementList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('achievement'), Lang::achievement('notFound'));
-
- $this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS));
-
- $this->name = $this->subject->getField('name', true);
- }
-
- protected function generatePath()
- {
- // create page title and path
- $curCat = $this->subject->getField('category');
- do
- {
- array_unshift($this->path, $curCat);
- $curCat = DB::Aowow()->SelectCell('SELECT parentCat FROM ?_achievementcategory WHERE id = ?d', $curCat);
- }
- while ($curCat > 0);
-
- array_unshift($this->path, 0, 9);
- $this->path[] = $this->subject->getField('typeCat');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('achievement')));
- }
-
- protected function generateContent()
- {
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // points
- if ($_ = $this->subject->getField('points'))
- $infobox[] = Lang::achievement('points').Lang::main('colon').'[achievementpoints='.$_.']';
-
- // location
- // todo (low)
-
- // faction
- $infobox[] = Lang::main('side').Lang::main('colon') . match ($this->subject->getField('faction'))
- {
- SIDE_ALLIANCE => '[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]',
- SIDE_HORDE => '[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]',
- default => Lang::game('si', SIDE_BOTH) // 0, 3
- };
-
- // icon
- if ($_ = $this->subject->getField('iconId'))
- {
- $infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
- $this->extendGlobalIds(Type::ICON, $_);
- }
-
- // realm first available?
- if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH))
- {
- $avlb = [];
- foreach (Profiler::getRealms() AS $rId => $rData)
- if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId))
- $avlb[] = Util::ucWords($rData['name']);
-
- if ($avlb)
- $infobox[] = Lang::achievement('rfAvailable').implode(', ', $avlb);
- }
-
- /**********/
- /* Series */
- /**********/
-
- $series = [];
-
- if ($c = $this->subject->getField('chainId'))
- {
- $chainAcv = new AchievementList(array(['chainId', $c]));
-
- foreach ($chainAcv->iterate() as $aId => $__)
- {
- $pos = $chainAcv->getField('chainPos');
- if (!isset($series[$pos]))
- $series[$pos] = [];
-
- $series[$pos][] = array(
- 'side' => $chainAcv->getField('faction'),
- 'typeStr' => Type::getFileString(Type::ACHIEVEMENT),
- 'typeId' => $aId,
- 'name' => $chainAcv->getField('name', true)
- );
- }
- }
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->headIcons = [$this->subject->getField('iconString')];
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->mail = $this->createMail($reqBook);
- $this->series = $series ? [[array_values($series), null]] : null;
- $this->description = $this->subject->getField('description', true);
- $this->redButtons = array(
- BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE),
- BUTTON_LINKS => array(
- 'linkColor' => 'ffffff00',
- 'linkId' => Type::getFileString(Type::ACHIEVEMENT).':'.$this->typeId.':"..UnitGUID("player")..":0:0:0:0:0:0:0:0',
- 'linkName' => $this->name,
- 'type' => $this->type,
- 'typeId' => $this->typeId
- )
- );
- $this->criteria = array(
- 'reqQty' => $this->subject->getField('reqCriteriaCount'),
- 'icons' => [],
- 'data' => []
- );
-
- if ($reqBook)
- $this->addScript([SC_CSS_FILE, 'css/Book.css']);
-
- // create rewards
- if ($foo = $this->subject->getField('rewards'))
- {
- if ($itemRewards = array_filter($foo, function($x) { return $x[0] == Type::ITEM; }))
- {
- $bar = new ItemList(array(['i.id', array_column($itemRewards, 1)]));
- foreach ($bar->iterate() as $id => $__)
- {
- $this->rewards['item'][] = array(
- 'name' => $bar->getField('name', true),
- 'quality' => $bar->getField('quality'),
- 'typeStr' => Type::getFileString(Type::ITEM),
- 'id' => $id,
- 'globalStr' => Type::getJSGlobalString(Type::ITEM)
- );
- }
- }
-
- if ($titleRewards = array_filter($foo, function($x) { return $x[0] == Type::TITLE; }))
- {
- $bar = new TitleList(array(['id', array_column($titleRewards, 1)]));
- foreach ($bar->iterate() as $__)
- $this->rewards['title'][] = sprintf(Lang::achievement('titleReward'), $bar->id, trim(str_replace('%s', '', $bar->getField('male', true))));
- }
- }
-
- $this->rewards['text'] = $this->subject->getField('reward', true);
-
- // factionchange-equivalent
- if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_achievement WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId))
- {
- $altAcv = new AchievementList(array(['id', abs($pendant)]));
- if (!$altAcv->error)
- {
- $this->transfer = sprintf(
- Lang::achievement('_transfer'),
- $altAcv->id,
- 1, // quality
- $altAcv->getField('iconString'),
- $altAcv->getField('name', true),
- $pendant > 0 ? 'alliance' : 'horde',
- $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2)
- );
- }
- }
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: see also
- $conditions = array(
- ['name_loc'.Lang::getLocale()->value, $this->subject->getField('name', true)],
- ['id', $this->typeId, '!']
- );
- $saList = new AchievementList($conditions);
- if (!$saList->error)
- {
- $this->extendGlobalData($saList->getJSGlobals());
-
- $this->lvTabs[] = [AchievementList::$brickFile, array(
- 'data' => array_values($saList->getListviewData()),
- 'id' => 'see-also',
- 'name' => '$LANG.tab_seealso',
- 'visibleCols' => ['category']
- )];
- }
-
- // tab: criteria of
- $refs = DB::Aowow()->SelectCol('SELECT refAchievementId FROM ?_achievementcriteria WHERE Type = ?d AND value1 = ?d',
- ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT,
- $this->typeId
- );
- if (!empty($refs))
- {
- $coList = new AchievementList(array(['id', $refs]));
- $this->lvTabs[] = [AchievementList::$brickFile, array(
- 'data' => array_values($coList->getListviewData()),
- 'id' => 'criteria-of',
- 'name' => '$LANG.tab_criteriaof',
- 'visibleCols' => ['category']
- )];
- $this->extendGlobalData($coList->getJSGlobals());
- }
-
- // tab: condition for
- $cnd = new Conditions();
- $cnd->getByCondition(Type::ACHIEVEMENT, $this->typeId)->prepare();
- if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
- {
- $this->extendGlobalData($cnd->getJsGlobals());
- $this->lvTabs[] = $tab;
- }
-
-
- /*****************/
- /* Criteria List */
- /*****************/
-
- $iconId = 1;
- $rightCol = [];
- $scripts = [];
-
- // serverside extra-Data
- if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
- {
- Util::checkNumeric($crtIds);
- $crtExtraData = DB::World()->select('SELECT criteria_id AS ARRAY_KEY, type AS ARRAY_KEY2, value1, value2, ScriptName FROM achievement_criteria_data WHERE criteria_id IN (?a)', $crtIds);
- }
- else
- $crtExtraData = [];
-
- foreach ($this->subject->getCriteria() as $i => $crt)
- {
- // hide hidden criteria for regular users (really do..?)
- // if (($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN) && User::$perms > 0)
- // continue;
-
- // alternative display option
- $displayMoney = $crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER;
- $crtName = Util::localizedString($crt, 'name');
- $tmp = array(
- 'id' => $crt['id'],
- 'name' => $crtName,
- 'type' => $crt['type'],
- );
-
- $obj = (int)$crt['value1'];
- $qty = (int)$crt['value2'];
-
- switch ($crt['type'])
- {
- // link to npc
- case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
- case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
- $tmp['link'] = array(
- 'href' => '?npc='.$obj,
- 'text' => $crtName,
- );
- $tmp['extraText'] = Lang::achievement('slain');
- break;
- // link to area (by map)
- case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
- case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
- case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
- case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
- case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
- if ($zoneId = DB::Aowow()->selectCell('SELECT id FROM ?_zones WHERE mapId = ? LIMIT 1', $obj))
- $tmp['link'] = array(
- 'href' => '?zone='.$zoneId,
- 'text' => $crtName,
- );
- else
- $tmp['extraText'] = $crtName;
- break;
- // link to area
- case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
- case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
- $tmp['link'] = array(
- 'href' => '?zone='.$obj,
- 'text' => $crtName,
- );
- break;
- // link to skills
- case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
- case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
- case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
- case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
- $tmp['link'] = array(
- 'href' => '?skill='.$obj,
- 'text' => $crtName,
- );
- break;
- // link to class
- case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
- $tmp['link'] = array(
- 'href' => '?class='.$obj,
- 'text' => $crtName,
- );
- break;
- // link to race
- case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
- $tmp['link'] = array(
- 'href' => '?race='.$obj,
- 'text' => $crtName,
- );
- break;
- // link to title - todo (low): crosslink
- case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
- $tmp['extraText'] = Util::ucFirst(Lang::game('title')).Lang::main('colon').$crtName;
- break;
- // link to achivement (/w icon)
- case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
- $tmp['link'] = array(
- 'href' => '?achievement='.$obj,
- 'text' => $crtName,
- );
- $tmp['icon'] = $iconId;
- $this->criteria['icons'][] = array(
- 'itr' => $iconId++,
- 'type' => Type::getJSGlobalString(Type::ACHIEVEMENT),
- 'id' => $obj,
- );
- $this->extendGlobalIds(Type::ACHIEVEMENT, $obj);
- break;
- // link to quest
- case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
- // $crtName = ;
- $tmp['link'] = array(
- 'href' => '?quest='.$obj,
- 'text' => $crtName ?: QuestList::getName($obj),
- );
- break;
- // link to spell (/w icon)
- case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
- case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
- case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
- case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
- case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
- $tmp['link'] = array(
- 'href' => '?spell='.$obj,
- 'text' => ($crtName ?: SpellList::getName($obj))
- );
- $this->extendGlobalIds(Type::SPELL, $obj);
- $tmp['icon'] = $iconId;
- $this->criteria['icons'][] = array(
- 'itr' => $iconId++,
- 'type' => Type::getJSGlobalString(Type::SPELL),
- 'id' => $obj,
- );
- break;
- // link to item (/w icon)
- case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
- case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
- case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
- case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
- $crtItm = new ItemList(array(['i.id', $obj]));
- $tmp['link'] = array(
- 'href' => '?item='.$obj,
- 'text' => ($crtName ?: $crtItm->getField('name', true)),
- 'quality' => $crtItm->getField('quality'),
- 'count' => $qty,
- );
- $this->extendGlobalData($crtItm->getJSGlobals());
- $tmp['icon'] = $iconId;
- $this->criteria['icons'][] = array(
- 'itr' => $iconId++,
- 'type' => Type::getJSGlobalString(Type::ITEM),
- 'id' => $obj,
- 'count' => $qty,
- );
- break;
- // link to faction (/w target reputation)
- case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
- $tmp['link'] = array(
- 'href' => '?faction='.$obj,
- 'text' => $crtName ?: FactionList::getName($obj),
- );
- $tmp['extraText'] = ' ('.Lang::getReputationLevelForPoints($qty).')';
- break;
- // link to GObject
- case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
- case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
- $tmp['link'] = array(
- 'href' => '?object='.$obj,
- 'text' => $crtName,
- );
- break;
- // link to emote
- case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
- $tmp['link'] = array(
- 'href' => '?emote='.$obj,
- 'text' => $crtName,
- );
- break;
- default:
- // Add a gold coin icon if required
- $tmp['extraText'] = $displayMoney ? Util::formatMoney($qty) : $crtName;
- break;
- }
-
- if (!empty($crtExtraData[$crt['id']]))
- {
- $tmp['extraData'] = [];
- foreach ($crtExtraData[$crt['id']] as $xType => $xData)
- {
- // just pick stuff, that can actually be linked
- switch ($xType)
- {
- case 1: // TYPE_T_CREATURE
- $tmp['extraData'][] = ['?npc='.$xData['value1'], CreatureList::getName($xData['value1'])];
- break;
- case 2: // TYPE_T_PLAYER_CLASS_RACE
- case 21: // TYPE_S_PLAYER_CLASS_RACE
- if ($xData['value1'])
- $tmp['extraData'][] = ['?class='.$xData['value1'], (new CharClassList(array(['id', $xData['value1']])))->getField('name', true)];
-
- if ($xData['value2'])
- $tmp['extraData'][] = ['?race='.$xData['value2'], (new CharRaceList(array(['id', $xData['value2']])))->getField('name', true)];
-
- break;
- // case 3: // TYPE_T_PLAYER_LESS_HEALTH
- // case 4: // TYPE_T_PLAYER_DEAD
- case 5: // TYPE_S_AURA
- case 7: // TYPE_T_AURA
- $tmp['extraData'][] = ['?spell='.$xData['value1'], SpellList::getName($xData['value1'])];
- break;
- case 6: // TYPE_S_AREA
- $tmp['extraData'][] = ['?zone='.$xData['value1'], ZoneList::getName($xData['value1'])];
- break;
- // case 8: // TYPE_VALUE
- // case 9: // TYPE_T_LEVEL
- // case 10: // TYPE_T_GENDER
- case 11: // TYPE_SCRIPT
- if ($xData['ScriptName'])
- $scripts[] = $xData['ScriptName'];
-
- break;
- // case 12: // TYPE_MAP_DIFFICULTY
- // case 13: // TYPE_MAP_PLAYER_COUNT
- // case 14: // TYPE_T_TEAM
- // case 15: // TYPE_S_DRUNK
- case 16: // TYPE_HOLIDAY
- if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
- $tmp['extraData'][] = ['?event='.$we->id, $we->getField('name', true)];
-
- break;
- // case 17: // TYPE_BG_LOSS_TEAM_SCORE
- // case 18: // TYPE_INSTANCE_SCRIPT
- // case 19: // TYPE_S_EQUIPED_ITEM
- case 20: // TYPE_MAP_ID
- if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
- $tmp['extraData'][] = ['?zone='.$z->id, $z->getField('name', true)];
-
- break;
- // case 22: // TYPE_NTH_BIRTHDAY
- case 23: // TYPE_S_KNOWN_TITLE
- $tmp['extraData'][] = ['?title='.$xData['value1'], trim(str_replace('%s', '', (new TitleList(array(['id', $xData['value1']])))->getField('male', true)))];
- break;
- }
-
- // moar stuffz
-
- }
- }
-
- // If the right column
- if ($i % 2)
- $this->criteria['data'][] = $tmp;
- else
- $rightCol[] = $tmp;
- }
-
- // If you found the second column - merge data from it to the end of the main body
- if ($rightCol)
- $this->criteria['data'] = array_merge($this->criteria['data'], $rightCol);
-
- // criteria have scripts
- if (User::isInGroup(U_GROUP_EMPLOYEE) && $scripts)
- {
- $s = '[li]Script'.Lang::main('colon').'[ul][li]'.implode('[/li][li]', array_unique($scripts)).'[/li][/ul][/li]';
- $this->infobox = substr_replace($this->infobox, $s, -5, 0);
- }
- }
-
- protected function generateTooltip()
- {
- $power = new \StdClass();
- if (!$this->subject->error)
- {
- $power->{'name_'.Lang::getLocale()->json()} = $this->subject->getField('name', true);
- $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
- $power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip();
- }
-
- return sprintf($this->powerTpl, $this->typeId, Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-
- private function createMail(&$reqCss = false)
- {
- $mail = [];
-
- if ($_ = $this->subject->getField('mailTemplate'))
- {
- $letter = DB::Aowow()->selectRow('SELECT * FROM ?_mails WHERE id = ?d', $_);
- if (!$letter)
- return [];
-
- $reqCss = true;
- $mail = array(
- 'id' => $_,
- 'delay' => null,
- 'sender' => null,
- 'attachments' => [],
- 'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
- 'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true))
- );
- }
- else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true)))
- {
- $reqCss = true;
- $mail = array(
- 'id' => -$this->typeId,
- 'delay' => null,
- 'sender' => null,
- 'attachments' => [],
- 'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
- 'text' => $_
- );
- }
-
- if ($_ = CreatureList::getName($this->subject->getField('sender')))
- $mail['sender'] = sprintf(Lang::mail('mailBy'), $this->subject->getField('sender'), $_);
-
- return $mail;
- }
-}
-
-?>
diff --git a/pages/achievements.php b/pages/achievements.php
deleted file mode 100644
index c716b867..00000000
--- a/pages/achievements.php
+++ /dev/null
@@ -1,139 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- protected $validCats = array(
- 92 => true,
- 96 => [14861, 14862, 14863],
- 97 => [14777, 14778, 14779, 14780],
- 95 => [165, 14801, 14802, 14803, 14804, 14881, 14901, 15003],
- 168 => [14808, 14805, 14806, 14921, 14922, 14923, 14961, 14962, 15001, 15002, 15041, 15042],
- 169 => [170, 171, 172],
- 201 => [14864, 14865, 14866],
- 155 => [160, 187, 159, 163, 161, 162, 158, 14981, 156, 14941],
- 81 => true,
- 1 => array (
- 130 => [140, 145, 147, 191],
- 141 => true,
- 128 => [135, 136, 137],
- 122 => [123, 124, 125, 126, 127],
- 133 => true,
- 14807 => [14821, 14822, 14823, 14963, 15021, 15062],
- 132 => [178, 173],
- 134 => true,
- 131 => true,
- 21 => [152, 153, 154]
- )
- );
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->filterObj = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
-
- $this->name = Util::ucFirst(Lang::game('achievements'));
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- // include child categories if current category is empty
- if ($this->category)
- $conditions[] = ['category', (int)end($this->category)];
-
- $this->filterObj->evalCriteria();
-
- if ($fiCnd = $this->filterObj->getConditions())
- $conditions[] = $fiCnd;
-
- $acvList = new AchievementList($conditions, ['calcTotal' => true]);
- if (!$acvList->getMatches())
- {
- $category = [!empty($this->category) ? (int)end($this->category) : 0];
- $conditions = [];
- if ($fiCnd)
- $conditions[] = $fiCnd;
- if ($catList = DB::Aowow()->SelectCol('SELECT Id FROM ?_achievementcategory WHERE parentCat IN (?a) OR parentCat2 IN (?a) ', $category, $category))
- $conditions[] = ['category', $catList];
-
- $acvList = new AchievementList($conditions, ['calcTotal' => true]);
- }
-
- $tabData = [];
- if (!$acvList->error)
- {
- $tabData['data'] = array_values($acvList->getListviewData());
-
- // fill g_items, g_titles, g_achievements
- $this->extendGlobalData($acvList->getJSGlobals());
-
- // if we are have different cats display field
- if ($acvList->hasDiffFields('category'))
- $tabData['visibleCols'] = ['category'];
-
- if ($this->filterObj->fiExtraCols)
- $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
-
- // create note if search limit was exceeded
- if ($acvList->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT'));
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
- }
-
- $this->lvTabs[] = [AchievementList::$brickFile, $tabData];
- }
-
- protected function postCache()
- {
- // sort for dropdown-menus in filter
- Lang::sort('game', 'si');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::game('achievements')));
- if ($this->category)
- array_unshift($this->title, Lang::achievement('cat', end($this->category)));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- foreach ($this->category as $cat)
- $this->path[] = $cat;
- }
-}
-
-?>
diff --git a/template/bricks/mail.tpl.php b/template/bricks/mail.tpl.php
index 643c2922..27bdbe48 100644
--- a/template/bricks/mail.tpl.php
+++ b/template/bricks/mail.tpl.php
@@ -3,24 +3,24 @@
use \Aowow\Lang;
-if ($m = $this->mail):
+if (['header' => $header, 'subject' => $subject, 'text' => $text, 'attachments' => $attachments] = $this->mail):
$offset ??= 0; // in case we have multiple icons on the page (prominently quest-rewards)
- echo '
'.Lang::mail('mailDelivery', $m['header'])."
\n";
+ echo ' '.Lang::mail('mailDelivery', $header)."
\n";
- if ($m['subject']):
- echo ' \n";
+ if ($subject):
+ echo ' \n";
endif;
- if ($m['text']):
- echo ' \n";
+ if ($text):
+ echo ' \n";
endif;
- if ($m['attachments']):
+ if ($attachments):
?>
renderContainer(20, $offset, true);
endforeach;
?>
@@ -28,8 +28,8 @@ if ($m = $this->mail):
diff --git a/template/pages/achievement.tpl.php b/template/pages/achievement.tpl.php
index d101b5bc..0d1f4df8 100644
--- a/template/pages/achievement.tpl.php
+++ b/template/pages/achievement.tpl.php
@@ -1,7 +1,10 @@
-
+brick('header'); ?>
+ use \Aowow\Lang;
+ $this->brick('header');
+?>
@@ -20,75 +23,29 @@ $this->brick('headIcons');
$this->brick('redButtons');
?>
-
=$this->name; ?>
+
=$this->h1; ?>
+ =$this->description.PHP_EOL; ?>
+
=Lang::achievement('criteria').($this->reqCrtQty ? ' – '.Lang::achievement('reqNumCrt', [$this->reqCrtQty, count($this->criteria)]).'' : ''); ?>
description;
-
- echo '
'.Lang::achievement('criteria').($this->criteria['reqQty'] ? ' – '.Lang::achievement('reqNumCrt').' '.$this->criteria['reqQty'].' '.Lang::achievement('outOf').' '.count($this->criteria['data']).'' : null)."
\n";
-?>
-
-
-
';
- $tbl2 = '
';
- $rows1 = '';
- $rows2 = '';
-
-foreach ($this->criteria['data'] as $i => $cr):
- if (isset($cr['icon'])):
- $row = '
| ';
- else:
- $row = '
|---|
| ';
- endif;
-
- $row .= '';
-
- if (!empty($cr['link'])):
- $row .= ''.Util::htmlEscape($cr['link']['text']).'';
- endif;
-
- if (!empty($cr['link']['count']) && $cr['link']['count'] > 1):
- $row .= ' ('.$cr['link']['count'].')';
- endif;
-
- if (isset($cr['extraText'])):
- $row .= ' '.$cr['extraText'];
- endif;
-
- $row .= '';
-
- if (!empty($cr['extraData'])):
- $buff = [];
- foreach ($cr['extraData'] as $xd):
- $buff[] = $xd[0] ? ''.$xd[1].'' : ''.$xd[1].'';
- endforeach;
-
- $row .= ' ('.implode(', ', $buff).')';
- endif;
-
- $row .= ' |
|---|
';
-
+$rows0 = $rows1 = '';
+foreach ($this->criteria as $i => $icon):
// every odd number of elements
- if ($i + 1 > round(count($this->criteria['data']) / 2)):
- $rows2 .= $row;
- else:
- $rows1 .= $row;
- endif;
+ ${'rows' . ($i % 2)} .= $icon->renderContainer(20, $i, true);
endforeach;
-if ($rows2):
- echo sprintf($tbl2, $rows2);
+if ($rows0):
+ echo "
\n";
endif;
if ($rows1):
- echo sprintf($tbl1, $rows1);
+ echo "
\n";
endif;
?>
@@ -96,29 +53,28 @@ endforeach;
rewards):
- if (!empty($r['item'])):
+if ([$rewItems, $rewTitle, $rewText] = $this->rewards):
+ if ($rewItems):
echo '
'.Lang::main('rewards')."
\n";
- $this->brick('rewards', ['rewards' => $r['item'], 'rewTitle' => null]);
+ $this->brick('rewards', ['rewards' => $rewItems, 'rewTitle' => null]);
endif;
- if (!empty($r['title'])):
+ if ($rewTitle):
echo '
'.Lang::main('gains')."
\n
";
- foreach ($r['title'] as $i):
+ foreach ($rewTitle as $i):
echo ' '.$i."
\n";
endforeach;
echo "
\n";
endif;
- if (empty($r['title']) && empty($r['item']) && $r['text']):
- echo '
'.Lang::main('rewards')."
\n" .
- '
\n";
+ if (!$rewTitle && !$rewItems && $rewText):
+ echo '
'.Lang::main('rewards')."
\n
\n";
endif;
endif;
-$this->brick('mail');
+$this->brickIf($this->mail, 'mail');
-if (!empty($this->transfer)):
+if ($this->transfer):
echo "
";
echo "
\n ".$this->transfer."\n";
endif;
@@ -129,7 +85,7 @@ endif;
brick('lvTabs', ['relTabs' => true]);
+$this->brick('lvTabs');
$this->brick('contribute');
?>
diff --git a/template/pages/achievements.tpl.php b/template/pages/achievements.tpl.php
index 500b7ac5..8688d9d3 100644
--- a/template/pages/achievements.tpl.php
+++ b/template/pages/achievements.tpl.php
@@ -1,10 +1,11 @@
-
-
brick('header');
-$f = $this->filterObj->values // shorthand
-?>
+ namespace Aowow\Template;
+ use \Aowow\Lang;
+
+$this->brick('header');
+$f = $this->filter->values; // shorthand
+?>
@@ -12,37 +13,38 @@ $f = $this->filterObj->values // shorthand
brick('announcement');
-$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [9]]);
+$this->brick('pageTemplate', ['fiQuery' => $this->filter->query, 'fiMenuItem' => [9]]);
?>
-
-
+
-brick('filter'); ?>
+=$this->renderFilter(12); ?>
brick('lvTabs'); ?>