Example #1
0
 protected function generateContent()
 {
     $_level = $this->subject->getField('level');
     $_minLevel = $this->subject->getField('minLevel');
     $_flags = $this->subject->getField('flags');
     $_specialFlags = $this->subject->getField('specialFlags');
     $_side = Util::sideByRaceMask($this->subject->getField('reqRaceMask'));
     /***********/
     /* Infobox */
     /***********/
     $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
     // event (todo: assign eventData)
     if ($_ = $this->subject->getField('eventId')) {
         $this->extendGlobalIds(TYPE_WORLDEVENT, $_);
         $infobox[] = Lang::game('eventShort') . Lang::main('colon') . '[event=' . $_ . ']';
     }
     // level
     if ($_level > 0) {
         $infobox[] = Lang::game('level') . Lang::main('colon') . $_level;
     }
     // reqlevel
     if ($_minLevel) {
         $lvl = $_minLevel;
         if ($_ = $this->subject->getField('maxLevel')) {
             $lvl .= ' - ' . $_;
         }
         $infobox[] = sprintf(Lang::game('reqLevel'), $lvl);
     }
     // loremaster (i dearly hope those flags cover every case...)
     if ($this->subject->getField('zoneOrSortBak') > 0 && !$this->subject->isRepeatable()) {
         $conditions = array(['ac.type', ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE], ['ac.value1', $this->subject->getField('zoneOrSortBak')], ['a.faction', $_side, '&']);
         $loremaster = new AchievementList($conditions);
         $this->extendGlobalData($loremaster->getJSGlobals(GLOBALINFO_SELF));
         switch ($loremaster->getMatches()) {
             case 0:
                 break;
             case 1:
                 $infobox[] = Lang::quest('loremaster') . Lang::main('colon') . '[achievement=' . $loremaster->id . ']';
                 break;
             default:
                 $lm = Lang::quest('loremaster') . Lang::main('colon') . '[ul]';
                 foreach ($loremaster->iterate() as $id => $__) {
                     $lm .= '[li][achievement=' . $id . '][/li]';
                 }
                 $infobox[] = $lm . '[/ul]';
                 break;
         }
     }
     // type (maybe expand uppon?)
     $_ = [];
     if ($_flags & QUEST_FLAG_DAILY) {
         $_[] = Lang::quest('daily');
     } else {
         if ($_flags & QUEST_FLAG_WEEKLY) {
             $_[] = Lang::quest('weekly');
         } else {
             if ($_specialFlags & QUEST_FLAG_SPECIAL_MONTHLY) {
                 $_[] = Lang::quest('monthly');
             }
         }
     }
     if ($t = $this->subject->getField('type')) {
         $_[] = Lang::quest('questInfo', $t);
     }
     if ($_) {
         $infobox[] = Lang::game('type') . Lang::main('colon') . implode(' ', $_);
     }
     // side
     $_ = Lang::main('side') . Lang::main('colon');
     switch ($_side) {
         case 3:
             $infobox[] = $_ . Lang::game('si', 3);
             break;
         case 2:
             $infobox[] = $_ . '[span class=icon-horde]' . Lang::game('si', 2) . '[/span]';
             break;
         case 1:
             $infobox[] = $_ . '[span class=icon-alliance]' . Lang::game('si', 1) . '[/span]';
             break;
     }
     // races
     if ($_ = Lang::getRaceString($this->subject->getField('reqRaceMask'), $__, $jsg, $n, false)) {
         $this->extendGlobalIds(TYPE_RACE, $jsg);
         $t = $n == 1 ? Lang::game('race') : Lang::game('races');
         $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_;
     }
     // classes
     if ($_ = Lang::getClassString($this->subject->getField('reqClassMask'), $jsg, $n, false)) {
         $this->extendGlobalIds(TYPE_CLASS, $jsg);
         $t = $n == 1 ? Lang::game('class') : Lang::game('classes');
         $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_;
     }
     // profession / skill
     if ($_ = $this->subject->getField('reqSkillId')) {
         $this->extendGlobalIds(TYPE_SKILL, $_);
         $sk = '[skill=' . $_ . ']';
         if ($_ = $this->subject->getField('reqSkillPoints')) {
             $sk .= ' (' . $_ . ')';
         }
         $infobox[] = Lang::quest('profession') . Lang::main('colon') . $sk;
     }
     // timer
     if ($_ = $this->subject->getField('timeLimit')) {
         $infobox[] = Lang::quest('timer') . Lang::main('colon') . Util::formatTime($_ * 1000);
     }
     $startEnd = DB::Aowow()->select('SELECT * FROM ?_quests_startend WHERE questId = ?d', $this->typeId);
     // start
     $start = '[icon name=quest_start' . ($this->subject->isDaily() ? '_daily' : '') . ']' . Lang::event('start') . Lang::main('colon') . '[/icon]';
     $s = [];
     foreach ($startEnd as $se) {
         if ($se['method'] & 0x1) {
             $this->extendGlobalIds($se['type'], $se['typeId']);
             $s[] = ($s ? '[span=invisible]' . $start . '[/span] ' : $start . ' ') . '[' . Util::$typeStrings[$se['type']] . '=' . $se['typeId'] . ']';
         }
     }
     if ($s) {
         $infobox[] = implode('[br]', $s);
     }
     // end
     $end = '[icon name=quest_end' . ($this->subject->isDaily() ? '_daily' : '') . ']' . Lang::event('end') . Lang::main('colon') . '[/icon]';
     $e = [];
     foreach ($startEnd as $se) {
         if ($se['method'] & 0x2) {
             $this->extendGlobalIds($se['type'], $se['typeId']);
             $e[] = ($e ? '[span=invisible]' . $end . '[/span] ' : $end . ' ') . '[' . Util::$typeStrings[$se['type']] . '=' . $se['typeId'] . ']';
         }
     }
     if ($e) {
         $infobox[] = implode('[br]', $e);
     }
     // Repeatable
     if ($_flags & QUEST_FLAG_REPEATABLE || $_specialFlags & QUEST_FLAG_SPECIAL_REPEATABLE) {
         $infobox[] = Lang::quest('repeatable');
     }
     // sharable | not sharable
     $infobox[] = $_flags & QUEST_FLAG_SHARABLE ? Lang::quest('sharable') : Lang::quest('notSharable');
     // Keeps you PvP flagged
     if ($this->subject->isPvPEnabled()) {
         $infobox[] = Lang::quest('keepsPvpFlag');
     }
     // difficulty (todo (low): formula unclear. seems to be [minLevel,] -4, -2, (level), +3, +(9 to 15))
     if ($_level > 0) {
         $_ = [];
         // red
         if ($_minLevel && $_minLevel < $_level - 4) {
             $_[] = '[color=q10]' . $_minLevel . '[/color]';
         }
         // orange
         if (!$_minLevel || $_minLevel < $_level - 2) {
             $_[] = '[color=r1]' . (!$_ && $_minLevel > $_level - 4 ? $_minLevel : $_level - 4) . '[/color]';
         }
         // yellow
         $_[] = '[color=r2]' . (!$_ && $_minLevel > $_level - 2 ? $_minLevel : $_level - 2) . '[/color]';
         // green
         $_[] = '[color=r3]' . ($_level + 3) . '[/color]';
         // grey (is about +/-1 level off)
         $_[] = '[color=r4]' . ($_level + 3 + ceil(12 * $_level / MAX_LEVEL)) . '[/color]';
         if ($_) {
             $infobox[] = Lang::game('difficulty') . Lang::main('colon') . implode('[small] &nbsp;[/small]', $_);
         }
     }
     $this->infobox = '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]';
     /**********/
     /* Series */
     /**********/
     // Quest Chain (are there cases where quests go in parallel?)
     $chain = array(array(array('side' => $_side, 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $this->typeId, 'name' => $this->name, '_next' => $this->subject->getField('nextQuestIdChain'))));
     $_ = $chain[0][0];
     while ($_) {
         if ($_ = DB::Aowow()->selectRow('SELECT id AS typeId, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8, reqRaceMask FROM ?_quests WHERE nextQuestIdChain = ?d', $_['typeId'])) {
             $n = Util::localizedString($_, 'name');
             array_unshift($chain, array(array('side' => Util::sideByRaceMask($_['reqRaceMask']), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $_['typeId'], 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n)));
         }
     }
     $_ = end($chain)[0];
     while ($_) {
         if ($_ = DB::Aowow()->selectRow('SELECT id AS typeId, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8, reqRaceMask, nextQuestIdChain AS _next FROM ?_quests WHERE id = ?d', $_['_next'])) {
             $n = Util::localizedString($_, 'name');
             array_push($chain, array(array('side' => Util::sideByRaceMask($_['reqRaceMask']), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $_['typeId'], 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n, '_next' => $_['_next'])));
         }
     }
     if (count($chain) > 1) {
         $this->series[] = [$chain, null];
     }
     // todo (low): sensibly merge the following lists into 'series'
     $listGen = function ($cnd) {
         $chain = [];
         $list = new QuestList($cnd);
         if ($list->error) {
             return null;
         }
         foreach ($list->iterate() as $id => $__) {
             $n = $list->getField('name', true);
             $chain[] = array(array('side' => Util::sideByRaceMask($list->getField('reqRaceMask')), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $id, 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n));
         }
         return $chain;
     };
     $extraLists = array(['reqQ', array('OR', ['AND', ['nextQuestId', $this->typeId], ['exclusiveGroup', 0, '<']], ['AND', ['id', $this->subject->getField('prevQuestId')], ['nextQuestIdChain', $this->typeId, '!']])], ['reqOneQ', array(['exclusiveGroup', 0, '>'], ['nextQuestId', $this->typeId])], ['opensQ', array('OR', ['AND', ['prevQuestId', $this->typeId], ['id', $this->subject->getField('nextQuestIdChain'), '!']], ['id', $this->subject->getField('nextQuestId')])], ['closesQ', array(['exclusiveGroup', 0, '!'], ['exclusiveGroup', $this->subject->getField('exclusiveGroup')], ['id', $this->typeId, '!'])], ['enablesQ', array(['prevQuestId', -$this->typeId])], ['enabledByQ', array(['id', -$this->subject->getField('prevQuestId')])]);
     foreach ($extraLists as $el) {
         if ($_ = $listGen($el[1])) {
             $this->series[] = [$_, sprintf(Util::$dfnString, Lang::quest($el[0] . 'Desc'), Lang::quest($el[0]))];
         }
     }
     /*******************/
     /* Objectives List */
     /*******************/
     $this->objectiveList = [];
     $this->providedItem = [];
     // gather ids for lookup
     $olItems = $olNPCs = $olGOs = $olFactions = [];
     // items
     $olItems[0] = array($this->subject->getField('sourceItemId'), $this->subject->getField('sourceItemCount'), false);
     for ($i = 1; $i < 7; $i++) {
         $id = $this->subject->getField('reqItemId' . $i);
         $qty = $this->subject->getField('reqItemCount' . $i);
         if (!$id || !$qty) {
             continue;
         }
         $olItems[$i] = [$id, $qty, $id == $olItems[0][0]];
     }
     if ($ids = array_column($olItems, 0)) {
         $olItemData = new ItemList(array(['id', $ids]));
         $this->extendGlobalData($olItemData->getJSGlobals(GLOBALINFO_SELF));
         $providedRequired = false;
         foreach ($olItems as $i => list($itemId, $qty, $provided)) {
             if (!$i || !$itemId || !in_array($itemId, $olItemData->getFoundIDs())) {
                 continue;
             }
             if ($provided) {
                 $providedRequired = true;
             }
             $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_ITEM], 'id' => $itemId, 'name' => $olItemData->json[$itemId]['name'], 'qty' => $qty > 1 ? $qty : 0, 'quality' => 7 - $olItemData->json[$itemId]['quality'], 'extraText' => $provided ? '&nbsp;(' . Lang::quest('provided') . ')' : '');
         }
         // if providd item is not required by quest, list it below other requirements
         if (!$providedRequired && $olItems[0][0] && in_array($olItems[0][0], $olItemData->getFoundIDs())) {
             $this->providedItem = array('id' => $olItems[0][0], 'name' => $olItemData->json[$olItems[0][0]]['name'], 'qty' => $olItems[0][1] > 1 ? $olItems[0][1] : 0, 'quality' => 7 - $olItemData->json[$olItems[0][0]]['quality']);
         }
     }
     // creature or GO...
     for ($i = 1; $i < 5; $i++) {
         $id = $this->subject->getField('reqNpcOrGo' . $i);
         $qty = $this->subject->getField('reqNpcOrGoCount' . $i);
         $altTxt = $this->subject->getField('objectiveText' . $i, true);
         if ($id > 0 && $qty) {
             $olNPCs[$id] = [$qty, $altTxt, []];
         } else {
             if ($id < 0 && $qty) {
                 $olGOs[-$id] = [$qty, $altTxt];
             }
         }
     }
     // .. creature kills
     if ($ids = array_keys($olNPCs)) {
         $olNPCData = new CreatureList(array('OR', ['id', $ids], ['killCredit1', $ids], ['killCredit2', $ids]));
         $this->extendGlobalData($olNPCData->getJSGlobals(GLOBALINFO_SELF));
         // create proxy-references
         foreach ($olNPCData->iterate() as $id => $__) {
             if ($p = $olNPCData->getField('KillCredit1')) {
                 if (isset($olNPCs[$p])) {
                     $olNPCs[$p][2][$id] = $olNPCData->getField('name', true);
                 }
             }
             if ($p = $olNPCData->getField('KillCredit2')) {
                 if (isset($olNPCs[$p])) {
                     $olNPCs[$p][2][$id] = $olNPCData->getField('name', true);
                 }
             }
         }
         foreach ($olNPCs as $i => $pair) {
             if (!$i || !in_array($i, $olNPCData->getFoundIDs())) {
                 continue;
             }
             $ol = array('typeStr' => Util::$typeStrings[TYPE_NPC], 'id' => $i, 'name' => $pair[1] ?: Util::localizedString($olNPCData->getEntry($i), 'name'), 'qty' => $pair[0] > 1 ? $pair[0] : 0, 'extraText' => $_specialFlags & QUEST_FLAG_SPECIAL_SPELLCAST || $pair[1] ? '' : ' ' . Lang::achievement('slain'), 'proxy' => $pair[2]);
             if ($pair[2]) {
                 // has proxies assigned, add yourself as another proxy
                 $ol['proxy'][$i] = Util::localizedString($olNPCData->getEntry($i), 'name');
             }
             $this->objectiveList[] = $ol;
         }
     }
     // .. GO interactions
     if ($ids = array_keys($olGOs)) {
         $olGOData = new GameObjectList(array(['id', $ids]));
         $this->extendGlobalData($olGOData->getJSGlobals(GLOBALINFO_SELF));
         foreach ($olNPCs as $i => $pair) {
             if (!$i || !in_array($i, $olGOData->getFoundIDs())) {
                 continue;
             }
             $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_OBJECT], 'id' => $i, 'name' => $pair[1] ?: Util::localizedString($olGOData->getEntry($i), 'name'), 'qty' => $pair[0] > 1 ? $pair[0] : 0);
         }
     }
     // reputation required
     for ($i = 1; $i < 3; $i++) {
         $id = $this->subject->getField('reqFactionId' . $i);
         $val = $this->subject->getField('reqFactionValue' . $i);
         if (!$id) {
             continue;
         }
         $olFactions[$id] = $val;
     }
     if ($ids = array_keys($olFactions)) {
         $olFactionsData = new FactionList(array(['id', $ids]));
         $this->extendGlobalData($olFactionsData->getJSGlobals(GLOBALINFO_SELF));
         foreach ($olFactions as $i => $val) {
             if (!$i || !in_array($i, $olFactionsData->getFoundIDs())) {
                 continue;
             }
             $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_FACTION], 'id' => $i, 'name' => Util::localizedString($olFactionsData->getEntry($i), 'name'), 'qty' => sprintf(Util::$dfnString, $val . ' ' . Lang::achievement('points'), Lang::getReputationLevelForPoints($val)), 'extraText' => '');
         }
     }
     // granted spell
     if ($_ = $this->subject->getField('sourceSpellId')) {
         $this->extendGlobalIds(TYPE_SPELL, $_);
         $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_SPELL], 'id' => $_, 'name' => SpellList::getName($_), 'qty' => 0, 'extraText' => '&nbsp;(' . Lang::quest('provided') . ')');
     }
     // required money
     if ($this->subject->getField('rewardOrReqMoney') < 0) {
         $this->objectiveList[] = ['text' => Lang::quest('reqMoney') . Lang::main('colon') . Util::formatMoney(abs($this->subject->getField('rewardOrReqMoney')))];
     }
     // required pvp kills
     if ($_ = $this->subject->getField('reqPlayerKills')) {
         $this->objectiveList[] = ['text' => Lang::quest('playerSlain') . '&nbsp;(' . $_ . ')'];
     }
     /**********/
     /* Mapper */
     /**********/
     $this->addJS('?data=zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']);
     /*
         TODO (GODDAMNIT): jeez..
     */
     // $startend + reqNpcOrGo[1-4]
     $this->map = null;
     // array(
     // 'data' => ['zone' => $this->typeId],
     // 'som'  => Util::toJSON($som)
     // );
     /****************/
     /* Main Content */
     /****************/
     $this->gains = $this->createGains();
     $this->mail = $this->createMail($maTab, $startEnd);
     $this->rewards = $this->createRewards();
     $this->objectives = $this->subject->parseText('objectives', false);
     $this->details = $this->subject->parseText('details', false);
     $this->offerReward = $this->subject->parseText('offerReward', false);
     $this->requestItems = $this->subject->parseText('requestItems', false);
     $this->completed = $this->subject->parseText('completed', false);
     $this->end = $this->subject->parseText('end', false);
     $this->suggestedPl = $this->subject->getField('suggestedPlayers');
     $this->unavailable = $_flags & QUEST_FLAG_UNAVAILABLE || $this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW;
     $this->redButtons = array(BUTTON_LINKS => ['color' => 'ffffff00', 'linkId' => 'quest:' . $this->typeId . ':' . $_level . ''], BUTTON_WOWHEAD => true);
     if ($maTab) {
         $this->lvTabs[] = $maTab;
     }
     // factionchange-equivalent
     if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_quests WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId)) {
         $altQuest = new QuestList(array(['id', abs($pendant)]));
         if (!$altQuest->error) {
             $this->transfer = sprintf(Lang::quest('_transfer'), $altQuest->id, $altQuest->getField('name', true), $pendant > 0 ? 'alliance' : 'horde', $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2));
         }
     }
     /**************/
     /* Extra Tabs */
     /**************/
     // tab: see also
     $seeAlso = new QuestList(array(['name_loc' . User::$localeId, '%' . $this->name . '%'], ['id', $this->typeId, '!']));
     if (!$seeAlso->error) {
         $this->extendGlobalData($seeAlso->getJSGlobals());
         $this->lvTabs[] = array('file' => 'quest', 'data' => $seeAlso->getListviewData(), 'params' => array('name' => '$LANG.tab_seealso', 'id' => 'see-also'));
     }
     // tab: criteria of
     $criteriaOf = new AchievementList(array(['ac.type', ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST], ['ac.value1', $this->typeId]));
     if (!$criteriaOf->error) {
         $this->extendGlobalData($criteriaOf->getJSGlobals());
         $this->lvTabs[] = array('file' => 'achievement', 'data' => $criteriaOf->getListviewData(), 'params' => array('name' => '$LANG.tab_criteriaof', 'id' => 'criteria-of'));
     }
     // tab: conditions
     $cnd = [];
     if ($_ = $this->subject->getField('reqMinRepFaction')) {
         $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0][] = [CND_REPUTATION_RANK, $_, 1 << Util::getReputationLevelForPoints($this->subject->getField('reqMinRepValue'))];
         $this->extendGlobalIds(TYPE_FACTION, $_);
     }
     if ($_ = $this->subject->getField('reqMaxRepFaction')) {
         $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0][] = [-CND_REPUTATION_RANK, $_, 1 << Util::getReputationLevelForPoints($this->subject->getField('reqMaxRepValue'))];
         $this->extendGlobalIds(TYPE_FACTION, $_);
     }
     $_ = Util::getServerConditions([CND_SRC_QUEST_ACCEPT, CND_SRC_QUEST_SHOW_MARK], null, $this->typeId);
     if (!empty($_[0])) {
         // awkward merger
         if (isset($_[0][CND_SRC_QUEST_ACCEPT][$this->typeId][0])) {
             if (isset($cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0])) {
                 $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0] = array_merge($cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0], $_[0][CND_SRC_QUEST_ACCEPT][$this->typeId][0]);
             } else {
                 $cnd[CND_SRC_QUEST_ACCEPT] = $_[0][CND_SRC_QUEST_ACCEPT];
             }
         }
         if (isset($_[0][CND_SRC_QUEST_SHOW_MARK])) {
             $cnd[CND_SRC_QUEST_SHOW_MARK] = $_[0][CND_SRC_QUEST_SHOW_MARK];
         }
         $this->extendGlobalData($_[1]);
     }
     if ($cnd) {
         $tab = "<script type=\"text/javascript\">\n" . "var markup = ConditionList.createTab(" . Util::toJSON($cnd) . ");\n" . "Markup.printHtml(markup, 'tab-conditions', { allow: Markup.CLASS_STAFF })" . "</script>";
         $this->lvTabs[] = array('file' => null, 'data' => $tab, 'params' => array('id' => 'conditions', 'name' => '$LANG.requires'));
     }
 }
Example #2
0
File: zone.php Project: saqar/aowow
 protected function generateContent()
 {
     $this->addJS('?data=zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']);
     /***********/
     /* Infobox */
     /***********/
     $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
     // City
     if ($this->subject->getField('flags') & 0x8 && !$this->subject->getField('parentArea')) {
         $infobox[] = Lang::zone('city');
     }
     // Auto repop
     if ($this->subject->getField('flags') & 0x1000 && !$this->subject->getField('parentArea')) {
         $infobox[] = Lang::zone('autoRez');
     }
     // Level
     if ($_ = $this->subject->getField('levelMin')) {
         if ($_ < $this->subject->getField('levelMax')) {
             $_ .= ' - ' . $this->subject->getField('levelMax');
         }
         $infobox[] = Lang::game('level') . Lang::main('colon') . $_;
     }
     // required Level
     if ($_ = $this->subject->getField('levelReq')) {
         if ($__ = $this->subject->getField('levelReqLFG')) {
             $buff = sprintf(Lang::zone('reqLevels'), $_, $__);
         } else {
             $buff = Lang::main('_reqLevel') . Lang::main('colon') . $_;
         }
         $infobox[] = $buff;
     }
     // Territory
     $_ = $this->subject->getField('faction');
     $__ = '%s';
     if ($_ == 0) {
         $__ = '[span class=icon-alliance]%s[/span]';
     } else {
         if ($_ == 1) {
             $__ = '[span class=icon-horde]%s[/span]';
         } else {
             if ($_ == 4) {
                 $__ = '[span class=icon-ffa]%s[/span]';
             }
         }
     }
     $infobox[] = Lang::zone('territory') . Lang::main('colon') . sprintf($__, Lang::zone('territories', $_));
     // Instance Type
     $infobox[] = Lang::zone('instanceType') . Lang::main('colon') . '[span class=icon-instance' . $this->subject->getField('type') . ']' . Lang::zone('instanceTypes', $this->subject->getField('type')) . '[/span]';
     // Heroic mode
     if ($_ = $this->subject->getField('levelHeroic')) {
         $infobox[] = '[icon preset=heroic]' . sprintf(Lang::zone('hcAvailable'), $_) . '[/icon]';
     }
     // number of players
     if ($_ = $this->subject->getField('maxPlayer')) {
         $infobox[] = Lang::zone('numPlayers') . Lang::main('colon') . ($_ == -2 ? '10/25' : $_);
     }
     // Attunement Quest/Achievements & Keys
     if ($attmnt = $this->subject->getField('attunes')) {
         foreach ($attmnt as $type => $ids) {
             $this->extendGlobalIds($type, array_map('abs', $ids));
             foreach ($ids as $id) {
                 if ($type == TYPE_ITEM) {
                     $infobox[] = Lang::zone('key', (int) ($id < 0)) . Lang::main('colon') . '[item=' . abs($id) . ']';
                 } else {
                     $infobox[] = Lang::zone('attunement', (int) ($id < 0)) . Lang::main('colon') . '[' . Util::$typeStrings[$type] . '=' . abs($id) . ']';
                 }
             }
         }
     }
     // Instances
     if ($_ = DB::Aowow()->selectCol('SELECT id FROM ?_zones WHERE parentAreaId = ?d AND (flags & ?d) = 0', $this->typeId, CUSTOM_EXCLUDE_FOR_LISTVIEW)) {
         $this->extendGlobalIds(TYPE_ZONE, $_);
         $infobox[] = Lang::maps('Instances') . Lang::main('colon') . "\n[zone=" . implode("], \n[zone=", $_) . ']';
     }
     // location (if instance)
     if ($pa = $this->subject->getField('parentAreaId')) {
         $paO = new ZoneList(array(['id', $pa]));
         if (!$paO->error) {
             $pins = str_pad($this->subject->getField('parentX') * 10, 3, '0', STR_PAD_LEFT) . str_pad($this->subject->getField('parentY') * 10, 3, '0', STR_PAD_LEFT);
             $infobox[] = Lang::zone('location') . Lang::main('colon') . '[lightbox=map zone=' . $pa . ' pins=' . $pins . ']' . $paO->getField('name', true) . '[/lightbox]';
         }
     }
     /*  has to be defined in an article, i think
     
         // faction(s) / Reputation Hub / Raid Faction
         // [li]Raid faction: [faction=1156][/li] || [li]Factions: [faction=1156]/[faction=1156][/li]
     
         // final boss
         // [li]Final boss: [icon preset=boss][npc=37226][/icon][/li]
     */
     /****************/
     /* Main Content */
     /****************/
     $addToMap = function ($what, $entry) use(&$som) {
         // entry always contains: type, id, name, level, coords[]
         if (!isset($som[$what][$entry['name']])) {
             // not found yet
             $som[$what][$entry['name']][] = $entry;
         } else {
             // check for identical floors
             foreach ($som[$what][$entry['name']] as &$byFloor) {
                 if ($byFloor['level'] != $entry['level']) {
                     continue;
                 }
                 // found existing floor, ammending coords
                 $byFloor['coords'][] = $entry['coords'][0];
                 break;
             }
             // floor not used yet, create it
             $som[$what][$entry['name']][] = $entry;
         }
     };
     if ($_ = $this->subject->getField('parentArea')) {
         $this->extraText = sprintf(Lang::zone('zonePartOf'), $_);
         $this->extendGlobalIds(TYPE_ZONE, $_);
     }
     // we cannot fetch spawns via lists. lists are grouped by entry
     $oSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, TYPE_OBJECT);
     $cSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, TYPE_NPC);
     $conditions = [CFG_SQL_LIMIT_NONE, ['s.areaId', $this->typeId]];
     if (!User::isInGroup(U_GROUP_STAFF)) {
         $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
     }
     $objectSpawns = new GameObjectList($conditions);
     $creatureSpawns = new CreatureList($conditions);
     $questsLV = $rewardsLV = [];
     // see if we can actually display a map
     $hasMap = file_exists('static/images/wow/maps/' . Util::$localeStrings[User::$localeId] . '/normal/' . $this->typeId . '.jpg');
     if (!$hasMap) {
         // try multilayered
         $hasMap = file_exists('static/images/wow/maps/' . Util::$localeStrings[User::$localeId] . '/normal/' . $this->typeId . '-1.jpg');
     }
     if (!$hasMap) {
         // try english fallback
         $hasMap = file_exists('static/images/wow/maps/enus/normal/' . $this->typeId . '.jpg');
     }
     if (!$hasMap) {
         // try english fallback, multilayered
         $hasMap = file_exists('static/images/wow/maps/enus/normal/' . $this->typeId . '-1.jpg');
     }
     if ($hasMap) {
         $som = [];
         foreach ($oSpawns as $spawn) {
             $tpl = $objectSpawns->getEntry($spawn['typeId']);
             if (!$tpl) {
                 continue;
             }
             $n = Util::localizedString($tpl, 'name');
             $what = '';
             switch ($tpl['typeCat']) {
                 case -3:
                     $what = 'herb';
                     break;
                 case -4:
                     $what = 'vein';
                     break;
                 case 9:
                     $what = 'book';
                     break;
                 case -6:
                     if ($tpl['spellFocusId'] == 1) {
                         $what = 'anvil';
                     } else {
                         if ($tpl['spellFocusId'] == 3) {
                             $what = 'forge';
                         }
                     }
                     break;
             }
             if ($what) {
                 $addToMap($what, array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_OBJECT, 'id' => $tpl['id']));
             }
             if ($tpl['startsQuests']) {
                 $started = new QuestList(array(['qse.method', 1, '&'], ['qse.type', TYPE_OBJECT], ['qse.typeId', $tpl['id']]));
                 if ($started->error) {
                     continue;
                 }
                 // store data for misc tabs
                 foreach ($started->getListviewData() as $id => $data) {
                     if (!empty($started->rewards[$id][TYPE_ITEM])) {
                         $rewardsLV = array_merge($rewardsLV, array_keys($started->rewards[$id][TYPE_ITEM]));
                     }
                     if (!empty($started->choices[$id][TYPE_ITEM])) {
                         $rewardsLV = array_merge($rewardsLV, array_keys($started->choices[$id][TYPE_ITEM]));
                     }
                     $questsLV[$id] = $data;
                 }
                 $this->extendGlobalData($started->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
                 if ($tpl['A'] != -1 & ($_ = $started->getSOMData(SIDE_ALLIANCE))) {
                     $addToMap('alliancequests', array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_OBJECT, 'id' => $tpl['id'], 'side' => ($tpl['A'] < 0 ? 0 : 0x1) | ($tpl['H'] < 0 ? 0 : 0x2), 'quests' => array_values($_)));
                 }
                 if ($tpl['H'] != -1 & ($_ = $started->getSOMData(SIDE_HORDE))) {
                     $addToMap('hordequests', array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_OBJECT, 'id' => $tpl['id'], 'side' => ($tpl['A'] < 0 ? 0 : 0x1) | ($tpl['H'] < 0 ? 0 : 0x2), 'quests' => array_values($_)));
                 }
             }
         }
         $flightNodes = [];
         foreach ($cSpawns as $spawn) {
             $tpl = $creatureSpawns->getEntry($spawn['typeId']);
             if (!$tpl) {
                 continue;
             }
             $n = Util::localizedString($tpl, 'name');
             $sn = Util::localizedString($tpl, 'subname');
             $what = '';
             if ($tpl['npcflag'] & NPC_FLAG_REPAIRER) {
                 $what = 'repair';
             } else {
                 if ($tpl['npcflag'] & NPC_FLAG_AUCTIONEER) {
                     $what = 'auctioneer';
                 } else {
                     if ($tpl['npcflag'] & NPC_FLAG_BANKER) {
                         $what = 'banker';
                     } else {
                         if ($tpl['npcflag'] & NPC_FLAG_BATTLEMASTER) {
                             $what = 'battlemaster';
                         } else {
                             if ($tpl['npcflag'] & NPC_FLAG_INNKEEPER) {
                                 $what = 'innkeeper';
                             } else {
                                 if ($tpl['npcflag'] & NPC_FLAG_TRAINER) {
                                     $what = 'trainer';
                                 } else {
                                     if ($tpl['npcflag'] & NPC_FLAG_VENDOR) {
                                         $what = 'vendor';
                                     } else {
                                         if ($tpl['npcflag'] & NPC_FLAG_FLIGHT_MASTER) {
                                             $flightNodes[$tpl['id']] = [$spawn['posX'], $spawn['posY']];
                                             $what = 'flightmaster';
                                         } else {
                                             if ($tpl['npcflag'] & NPC_FLAG_STABLE_MASTER) {
                                                 $what = 'stablemaster';
                                             } else {
                                                 if ($tpl['npcflag'] & NPC_FLAG_GUILD_MASTER) {
                                                     $what = 'guildmaster';
                                                 } else {
                                                     if ($tpl['npcflag'] & (NPC_FLAG_SPIRIT_HEALER | NPC_FLAG_SPIRIT_GUIDE)) {
                                                         $what = 'spirithealer';
                                                     } else {
                                                         if ($creatureSpawns->isBoss()) {
                                                             $what = 'boss';
                                                         } else {
                                                             if ($tpl['rank'] == 2 || $tpl['rank'] == 4) {
                                                                 $what = 'rare';
                                                             }
                                                         }
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
             if ($what) {
                 $addToMap($what, array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_NPC, 'id' => $tpl['id'], 'reacthorde' => $tpl['H'] ?: 1, 'reactalliance' => $tpl['A'] ?: 1, 'description' => $sn));
             }
             if ($tpl['startsQuests']) {
                 $started = new QuestList(array(['qse.method', 1, '&'], ['qse.type', TYPE_NPC], ['qse.typeId', $tpl['id']]));
                 if ($started->error) {
                     continue;
                 }
                 // store data for misc tabs
                 foreach ($started->getListviewData() as $id => $data) {
                     if (!empty($started->rewards[$id][TYPE_ITEM])) {
                         $rewardsLV = array_merge($rewardsLV, array_keys($started->rewards[$id][TYPE_ITEM]));
                     }
                     if (!empty($started->choices[$id][TYPE_ITEM])) {
                         $rewardsLV = array_merge($rewardsLV, array_keys($started->choices[$id][TYPE_ITEM]));
                     }
                     $questsLV[$id] = $data;
                 }
                 if ($tpl['A'] != -1 & ($_ = $started->getSOMData(SIDE_ALLIANCE))) {
                     $addToMap('alliancequests', array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_NPC, 'id' => $tpl['id'], 'reacthorde' => $tpl['H'], 'reactalliance' => $tpl['A'], 'side' => ($tpl['A'] < 0 ? 0 : SIDE_ALLIANCE) | ($tpl['H'] < 0 ? 0 : SIDE_HORDE), 'quests' => array_values($_)));
                 }
                 if ($tpl['H'] != -1 & ($_ = $started->getSOMData(SIDE_HORDE))) {
                     $addToMap('hordequests', array('coords' => [[$spawn['posX'], $spawn['posY']]], 'level' => $spawn['floor'], 'name' => $n, 'type' => TYPE_NPC, 'id' => $tpl['id'], 'reacthorde' => $tpl['H'], 'reactalliance' => $tpl['A'], 'side' => ($tpl['A'] < 0 ? 0 : SIDE_ALLIANCE) | ($tpl['H'] < 0 ? 0 : SIDE_HORDE), 'quests' => array_values($_)));
                 }
             }
         }
         // remove unwanted indizes
         foreach ($som as $what => &$dataz) {
             if (empty($som[$what])) {
                 continue;
             }
             foreach ($dataz as &$data) {
                 $data = array_values($data);
             }
             if (!in_array($what, ['vein', 'herb', 'rare'])) {
                 $foo = [];
                 foreach ($dataz as $d) {
                     foreach ($d as $_) {
                         $foo[] = $_;
                     }
                 }
                 $dataz = $foo;
             }
         }
         // append paths between nodes
         if ($flightNodes) {
             // neutral nodes come last as the line is colored by the node it's attached to
             usort($som['flightmaster'], function ($a, $b) {
                 $n1 = $a['reactalliance'] == $a['reacthorde'];
                 $n2 = $b['reactalliance'] == $b['reacthorde'];
                 if ($n1 && !$n2) {
                     return 1;
                 }
                 if (!$n1 && $n2) {
                     return -1;
                 }
                 return 0;
             });
             $paths = DB::Aowow()->select('SELECT n1.typeId AS "0", n2.typeId AS "1" FROM ?_taxipath p JOIN ?_taxinodes n1 ON n1.id = p.startNodeId JOIN ?_taxinodes n2 ON n2.id = p.endNodeId WHERE n1.typeId IN (?a) AND n2.typeId IN (?a)', array_keys($flightNodes), array_keys($flightNodes));
             foreach ($paths as $k => $path) {
                 foreach ($som['flightmaster'] as &$fm) {
                     if ($fm['id'] != $path[0] && $fm['id'] != $path[1]) {
                         continue;
                     }
                     if ($fm['id'] == $path[0]) {
                         $fm['paths'][] = $flightNodes[$path[1]];
                     }
                     if ($fm['id'] == $path[1]) {
                         $fm['paths'][] = $flightNodes[$path[0]];
                     }
                     unset($paths[$k]);
                     break;
                 }
             }
         }
         // preselect bosses for raids/dungeons
         if (in_array($this->subject->getField('type'), [2, 3, 4, 5, 7, 8])) {
             $som['instance'] = true;
         }
         $this->map = array('data' => ['parent' => 'mapper-generic', 'zone' => $this->typeId], 'som' => $som);
     } else {
         $this->map = false;
     }
     $this->infobox = $infobox ? '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]' : null;
     $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
     $this->redButtons = array(BUTTON_WOWHEAD => true, BUTTON_LINKS => true);
     /*
        - associated with holiday?
     */
     /**************/
     /* Extra Tabs */
     /**************/
     // tab: NPCs
     if ($cSpawns && !$creatureSpawns->error) {
         $lvData = array('file' => 'creature', 'data' => $creatureSpawns->getListviewData(), 'params' => ['note' => sprintf(Util::$filterResultString, '?npcs&filter=cr=6;crs=' . $this->typeId . ';crv=0')]);
         if ($creatureSpawns->getMatches() > CFG_SQL_LIMIT_DEFAULT) {
             $lvData['params']['_truncated'] = 1;
         }
         $this->extendGlobalData($creatureSpawns->getJSGlobals(GLOBALINFO_SELF));
         $this->lvTabs[] = $lvData;
     }
     // tab: Objects
     if ($oSpawns && !$objectSpawns->error) {
         $lvData = array('file' => 'object', 'data' => $objectSpawns->getListviewData(), 'params' => ['note' => sprintf(Util::$filterResultString, '?objects&filter=cr=1;crs=' . $this->typeId . ';crv=0')]);
         if ($objectSpawns->getMatches() > CFG_SQL_LIMIT_DEFAULT) {
             $lvData['params']['_truncated'] = 1;
         }
         $this->extendGlobalData($objectSpawns->getJSGlobals(GLOBALINFO_SELF));
         $this->lvTabs[] = $lvData;
     }
     // tab: Quests [data collected by SOM-routine]
     if ($questsLV) {
         $this->lvTabs[] = array('file' => 'quest', 'data' => $questsLV, 'params' => ['note' => '$$WH.sprintf(LANG.lvnote_zonequests, ' . $this->subject->getField('mapId') . ', ' . $this->typeId . ', \'' . Util::jsEscape($this->subject->getField('name', true)) . '\', ' . $this->typeId . ')']);
     }
     // tab: item-quest starter
     // select every quest starter, that is a drop
     $questStartItem = DB::Aowow()->select('
         SELECT qse.typeId AS ARRAY_KEY, moreType, moreTypeId, moreZoneId
         FROM   ?_quests_startend qse JOIN ?_source src ON src.type = qse.type AND src.typeId = qse.typeId
         WHERE  src.src2 IS NOT NULL AND qse.type = ?d AND (moreZoneId = ?d OR (moreType = ?d AND moreTypeId IN (?a)) OR (moreType = ?d AND moreTypeId IN (?a)))', TYPE_ITEM, $this->typeId, TYPE_NPC, array_unique(array_column($cSpawns, 'typeId')) ?: [0], TYPE_OBJECT, array_unique(array_column($oSpawns, 'typeId')) ?: [0]);
     if ($questStartItem) {
         $qsiList = new ItemList(array(['id', array_keys($questStartItem)]));
         if (!$qsiList->error) {
             $this->lvTabs[] = array('file' => 'item', 'data' => $qsiList->getListviewData(), 'params' => ['name' => '$LANG.tab_startsquest', 'id' => 'starts-quest']);
             $this->extendGlobalData($qsiList->getJSGlobals(GLOBALINFO_SELF));
         }
     }
     // tab: Quest Rewards [ids collected by SOM-routine]
     if ($rewardsLV) {
         $rewards = new ItemList(array(['id', array_unique($rewardsLV)]));
         if (!$rewards->error) {
             $this->lvTabs[] = array('file' => 'item', 'data' => $rewards->getListviewData(), 'params' => ['name' => '$LANG.tab_questrewards', 'id' => 'quest-rewards', 'note' => sprintf(Util::$filterResultString, '?items&filter=cr=126;crs=' . $this->typeId . ';crv=0')]);
             $this->extendGlobalData($rewards->getJSGlobals(GLOBALINFO_SELF));
         }
     }
     // tab: achievements
     // tab: fished in zone
     $fish = new Loot();
     if ($fish->getByContainer(LOOT_FISHING, $this->typeId)) {
         $this->extendGlobalData($fish->jsGlobals);
         $xCols = array_merge(['Listview.extraCols.percent'], $fish->extraCols);
         foreach ($fish->iterate() as $lv) {
             if (!$lv['quest']) {
                 continue;
             }
             $xCols = array_merge($xCols, ['Listview.extraCols.condition']);
             $reqQuest[$lv['id']] = 0;
             $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
         }
         $this->lvTabs[] = array('file' => 'item', 'data' => $fish->getResult(), 'params' => ['name' => '$LANG.tab_fishing', 'id' => 'fishing', 'extraCols' => $xCols ? "\$[" . implode(', ', array_unique($xCols)) . "]" : null, 'hiddenCols' => "\$['side']"]);
     }
     // tab: spells
     if ($saData = DB::World()->select('SELECT * FROM spell_area WHERE area = ?d', $this->typeId)) {
         $spells = new SpellList(array(['id', array_column($saData, 'spell')]));
         if (!$spells->error) {
             $lvSpells = $spells->getListviewData();
             $this->extendGlobalData($spells->getJSGlobals());
             $extra = false;
             foreach ($saData as $a) {
                 if (empty($lvSpells[$a['spell']])) {
                     continue;
                 }
                 $condition = [];
                 if ($a['aura_spell']) {
                     $this->extendGlobalIds(TYPE_SPELL, abs($a['aura_spell']));
                     $condition[0][$this->typeId][] = [[$a['aura_spell'] > 0 ? CND_AURA : -CND_AURA, abs($a['aura_spell'])]];
                 }
                 if ($a['quest_start']) {
                     $this->extendGlobalIds(TYPE_QUEST, $a['quest_start']);
                     $group = [];
                     for ($i = 0; $i < 7; $i++) {
                         if (!($a['quest_start_status'] & 1 << $i)) {
                             continue;
                         }
                         if ($i == 0) {
                             $group[] = [CND_QUEST_NONE, $a['quest_start']];
                         } else {
                             if ($i == 1) {
                                 $group[] = [CND_QUEST_COMPLETE, $a['quest_start']];
                             } else {
                                 if ($i == 3) {
                                     $group[] = [CND_QUESTTAKEN, $a['quest_start']];
                                 } else {
                                     if ($i == 6) {
                                         $group[] = [CND_QUESTREWARDED, $a['quest_start']];
                                     }
                                 }
                             }
                         }
                     }
                     if ($group) {
                         $condition[0][$this->typeId][] = $group;
                     }
                 }
                 if ($a['quest_end'] && $a['quest_end'] != $a['quest_start']) {
                     $this->extendGlobalIds(TYPE_QUEST, $a['quest_end']);
                     $group = [];
                     for ($i = 0; $i < 7; $i++) {
                         if (!($a['quest_end_status'] & 1 << $i)) {
                             continue;
                         }
                         if ($i == 0) {
                             $group[] = [-CND_QUEST_NONE, $a['quest_end']];
                         } else {
                             if ($i == 1) {
                                 $group[] = [-CND_QUEST_COMPLETE, $a['quest_end']];
                             } else {
                                 if ($i == 3) {
                                     $group[] = [-CND_QUESTTAKEN, $a['quest_end']];
                                 } else {
                                     if ($i == 6) {
                                         $group[] = [-CND_QUESTREWARDED, $a['quest_end']];
                                     }
                                 }
                             }
                         }
                     }
                     if ($group) {
                         $condition[0][$this->typeId][] = $group;
                     }
                 }
                 if ($a['racemask']) {
                     $foo = [];
                     for ($i = 0; $i < 11; $i++) {
                         if ($a['racemask'] & 1 << $i) {
                             $foo[] = $i + 1;
                         }
                     }
                     $this->extendGlobalIds(TYPE_RACE, $foo);
                     $condition[0][$this->typeId][] = [[CND_RACE, $a['racemask']]];
                 }
                 if ($a['gender'] != 2) {
                     // 2: both
                     $condition[0][$this->typeId][] = [[CND_GENDER, $a['gender'] + 1]];
                 }
                 if ($condition) {
                     $extra = true;
                     $lvSpells[$a['spell']] = array_merge($lvSpells[$a['spell']], ['condition' => $condition]);
                 }
             }
             $this->lvTabs[] = array('file' => 'spell', 'data' => $lvSpells, 'params' => array('extraCols' => $extra ? '$[Listview.extraCols.condition]' : null, 'hiddenCols' => "\$['skill']"));
         }
     }
     // tab: subzones
     $subZones = new ZoneList(array(['parentArea', $this->typeId]));
     if (!$subZones->error) {
         $this->lvTabs[] = array('file' => 'zone', 'data' => $subZones->getListviewData(), 'params' => ['name' => '$LANG.tab_zones', 'id' => 'subzones', 'hiddenCols' => "\$['territory', 'instancetype']"]);
         $this->extendGlobalData($subZones->getJSGlobals(GLOBALINFO_SELF));
     }
 }