コード例 #1
0
ファイル: npc.php プロジェクト: saqar/aowow
 protected function generateContent()
 {
     $this->addJS('?data=zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']);
     $_typeFlags = $this->subject->getField('typeFlags');
     $_altIds = [];
     $_altNPCs = null;
     $placeholder = null;
     $accessory = [];
     // difficulty entries of self
     if ($this->subject->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY) {
         $placeholder = [$this->subject->getField('parentId'), $this->subject->getField('parent', true)];
     } else {
         for ($i = 1; $i < 4; $i++) {
             if ($_ = $this->subject->getField('difficultyEntry' . $i)) {
                 $_altIds[$_] = $i;
             }
         }
         if ($_altIds) {
             $_altNPCs = new CreatureList(array(['id', array_keys($_altIds)]));
         }
     }
     if ($_ = DB::World()->selectCol('SELECT DISTINCT entry FROM vehicle_template_accessory WHERE accessory_entry = ?d', $this->typeId)) {
         $vehicles = new CreatureList(array(['id', $_]));
         foreach ($vehicles->iterate() as $id => $__) {
             $accessory[] = [$id, $vehicles->getField('name', true)];
         }
     }
     // try to determine, if it's spawned in a dungeon or raid (shaky at best, if spawned by script)
     $mapType = 0;
     if ($maps = DB::Aowow()->selectCol('SELECT DISTINCT areaId from ?_spawns WHERE type = ?d AND typeId = ?d', TYPE_NPC, $this->typeId)) {
         if (count($maps) == 1) {
             switch (DB::Aowow()->selectCell('SELECT `type` FROM ?_zones WHERE id = ?d', $maps[0])) {
                 case 2:
                 case 5:
                     $mapType = 1;
                     break;
                 case 3:
                 case 7:
                 case 8:
                     $mapType = 2;
                     break;
             }
         }
     } else {
         if ($_altIds) {
             if (count($_altIds) > 1) {
                 // 3 or more version -> definitly raid (10/25 + hc)
                 $mapType = 2;
             } else {
                 // 2 versions; may be Heroic (use this), but may also be 10/25-raid
                 $mapType = 1;
             }
         }
     }
     /***********/
     /* Infobox */
     /***********/
     $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
     // Event (ignore events, where the object only gets removed)
     if ($_ = DB::World()->selectCol('SELECT DISTINCT ge.eventEntry FROM game_event ge, game_event_creature gec, creature c WHERE ge.eventEntry = gec.eventEntry AND c.guid = gec.guid AND c.id = ?d', $this->typeId)) {
         $this->extendGlobalIds(TYPE_WORLDEVENT, $_);
         $ev = [];
         foreach ($_ as $i => $e) {
             $ev[] = ($i % 2 ? '[br]' : ' ') . '[event=' . $e . ']';
         }
         $infobox[] = Util::ucFirst(Lang::game('eventShort')) . Lang::main('colon') . implode(',', $ev);
     }
     // Level
     if ($this->subject->getField('rank') != NPC_RANK_BOSS) {
         $level = $this->subject->getField('minLevel');
         $maxLvl = $this->subject->getField('maxLevel');
         if ($level < $maxLvl) {
             $level .= ' - ' . $maxLvl;
         }
     } else {
         // Boss Level
         $level = '??';
     }
     $infobox[] = Lang::game('level') . Lang::main('colon') . $level;
     // Classification
     if ($_ = $this->subject->getField('rank')) {
         $str = $_typeFlags & 0x4 ? '[span class=icon-boss]' . Lang::npc('rank', $_) . '[/span]' : Lang::npc('rank', $_);
         $infobox[] = Lang::npc('classification') . Lang::main('colon') . $str;
     }
     // Reaction
     $_ = function ($r) {
         if ($r == 1) {
             return 2;
         }
         if ($r == -1) {
             return 10;
         }
         return;
     };
     $infobox[] = Lang::npc('react') . Lang::main('colon') . '[color=q' . $_($this->subject->getField('A')) . ']A[/color] [color=q' . $_($this->subject->getField('H')) . ']H[/color]';
     // Faction
     $this->extendGlobalIds(TYPE_FACTION, $this->subject->getField('factionId'));
     $infobox[] = Util::ucFirst(Lang::game('faction')) . Lang::main('colon') . '[faction=' . $this->subject->getField('factionId') . ']';
     // Tameable
     if ($_typeFlags & 0x1) {
         if ($_ = $this->subject->getField('family')) {
             $infobox[] = sprintf(Lang::npc('tameable'), '[url=pet=' . $_ . ']' . Lang::game('fa', $_) . '[/url]');
         }
     }
     // Wealth
     if ($_ = intVal(($this->subject->getField('minGold') + $this->subject->getField('maxGold')) / 2)) {
         $infobox[] = Lang::npc('worth') . Lang::main('colon') . '[tooltip=tooltip_avgmoneydropped][money=' . $_ . '][/tooltip]';
     }
     // is Vehicle
     if ($this->subject->getField('vehicleId')) {
         $infobox[] = Lang::npc('vehicle');
     }
     // AI
     if (User::isInGroup(U_GROUP_EMPLOYEE)) {
         if ($_ = $this->subject->getField('scriptName')) {
             $infobox[] = 'Script' . Lang::main('colon') . $_;
         } else {
             if ($_ = $this->subject->getField('aiName')) {
                 $infobox[] = 'AI' . Lang::main('colon') . $_;
             }
         }
     }
     if (User::isInGroup(U_GROUP_STAFF)) {
         // Mechanic immune
         if ($immuneMask = $this->subject->getField('mechanicImmuneMask')) {
             $buff = [];
             for ($i = 0; $i < 31; $i++) {
                 if ($immuneMask & 1 << $i) {
                     $buff[] = (!fMod(count($buff), 3) ? "\n" : null) . '[url=?spells&filter=me=' . ($i + 1) . ']' . Lang::game('me', $i + 1) . '[/url]';
                 }
             }
             $infobox[] = 'Not affected by mechanic' . Lang::main('colon') . implode(', ', $buff);
         }
         // extra flags
         if ($flagsExtra = $this->subject->getField('flagsExtra')) {
             $buff = [];
             if ($flagsExtra & 0x1) {
                 $buff[] = 'Binds attacker to instance on death';
             }
             if ($flagsExtra & 0x2) {
                 $buff[] = "[tooltip name=civilian]- does not aggro\n- death costs Honor[/tooltip][span class=tip tooltip=civilian]Civilian[/span]";
             }
             if ($flagsExtra & 0x4) {
                 $buff[] = 'Cannot parry';
             }
             if ($flagsExtra & 0x8) {
                 $buff[] = 'Has no parry haste';
             }
             if ($flagsExtra & 0x10) {
                 $buff[] = 'Cannot block';
             }
             if ($flagsExtra & 0x20) {
                 $buff[] = 'Cannot deal Crushing Blows';
             }
             if ($flagsExtra & 0x40) {
                 $buff[] = 'Rewards no experience';
             }
             if ($flagsExtra & 0x80) {
                 $buff[] = 'Trigger-Creature';
             }
             if ($flagsExtra & 0x100) {
                 $buff[] = 'Immune to Taunt';
             }
             if ($flagsExtra & 0x8000) {
                 $buff[] = "[tooltip name=guard]- engages PvP-Attacker\n- ignores enemy stealth, invisibility and Feign Death[/tooltip][span class=tip tooltip=guard]Guard[/span]";
             }
             if ($flagsExtra & 0x20000) {
                 $buff[] = 'Cannot deal Critical Hits';
             }
             if ($flagsExtra & 0x40000) {
                 $buff[] = 'Attacker does not gain weapon skill';
             }
             if ($flagsExtra & 0x80000) {
                 $buff[] = 'Taunt has diminishing returns';
             }
             if ($flagsExtra & 0x100000) {
                 $buff[] = 'Is subject to diminishing returns';
             }
             if ($buff) {
                 $infobox[] = 'Extra Flags' . Lang::main('colon') . '[ul][li]' . implode('[/li][li]', $buff) . '[/li][/ul]';
             }
         }
     }
     // > Stats
     $stats = [];
     $modes = [];
     // get difficulty versions if set
     $hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]';
     $modeRow = '[tr][td]%s&nbsp;&nbsp;[/td][td]%s[/td][/tr]';
     // Health
     $health = $this->subject->getBaseStats('health');
     $stats['health'] = Util::ucFirst(Lang::spell('powerTypes', -2)) . Lang::main('colon') . ($health[0] < $health[1] ? Lang::nf($health[0]) . ' - ' . Lang::nf($health[1]) : Lang::nf($health[0]));
     // Mana (may be 0)
     $mana = $this->subject->getBaseStats('power');
     $stats['mana'] = $mana[0] ? Lang::spell('powerTypes', 0) . Lang::main('colon') . ($mana[0] < $mana[1] ? Lang::nf($mana[0]) . ' - ' . Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
     // Armor
     $armor = $this->subject->getBaseStats('armor');
     $stats['armor'] = Lang::npc('armor') . Lang::main('colon') . ($armor[0] < $armor[1] ? Lang::nf($armor[0]) . ' - ' . Lang::nf($armor[1]) : Lang::nf($armor[0]));
     // Melee Damage
     $melee = $this->subject->getBaseStats('melee');
     if ($_ = $this->subject->getField('dmgSchool')) {
         // magic damage
         $stats['melee'] = Lang::npc('melee') . Lang::main('colon') . Lang::nf($melee[0]) . ' - ' . Lang::nf($melee[1]) . ' (' . Lang::game('sc', $_) . ')';
     } else {
         // phys. damage
         $stats['melee'] = Lang::npc('melee') . Lang::main('colon') . Lang::nf($melee[0]) . ' - ' . Lang::nf($melee[1]);
     }
     // Ranged Damage
     $ranged = $this->subject->getBaseStats('ranged');
     $stats['ranged'] = Lang::npc('ranged') . Lang::main('colon') . Lang::nf($ranged[0]) . ' - ' . Lang::nf($ranged[1]);
     if (in_array($mapType, [1, 2])) {
         foreach ($_altIds as $id => $mode) {
             foreach ($_altNPCs->iterate() as $dId => $__) {
                 if ($dId != $id) {
                     continue;
                 }
                 $m = Lang::npc('modes', $mapType, $mode);
                 // Health
                 $health = $_altNPCs->getBaseStats('health');
                 $modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? Lang::nf($health[0]) . ' - ' . Lang::nf($health[1]) : Lang::nf($health[0]));
                 // Mana (may be 0)
                 $mana = $_altNPCs->getBaseStats('power');
                 $modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? Lang::nf($mana[0]) . ' - ' . Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
                 // Armor
                 $armor = $_altNPCs->getBaseStats('armor');
                 $modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? Lang::nf($armor[0]) . ' - ' . Lang::nf($armor[1]) : Lang::nf($armor[0]));
                 // Melee Damage
                 $melee = $_altNPCs->getBaseStats('melee');
                 if ($_ = $_altNPCs->getField('dmgSchool')) {
                     // magic damage
                     $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]) . ' - ' . Lang::nf($melee[1]) . ' (' . Lang::game('sc', $_) . ')');
                 } else {
                     // phys. damage
                     $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]) . ' - ' . Lang::nf($melee[1]));
                 }
                 // Ranged Damage
                 $ranged = $_altNPCs->getBaseStats('ranged');
                 $modes['ranged'][] = sprintf($modeRow, $m, Lang::nf($ranged[0]) . ' - ' . Lang::nf($ranged[1]));
             }
         }
     }
     if ($modes) {
         foreach ($stats as $k => $v) {
             if ($v) {
                 $stats[$k] = sprintf($hint, implode('[/tr][tr]', $modes[$k]), $v, $k);
             }
         }
     }
     // < Stats
     if ($stats) {
         $infobox[] = Lang::npc('stats') . ($modes ? ' (' . Lang::npc('modes', $mapType, 0) . ')' : null) . Lang::main('colon') . '[ul][li]' . implode('[/li][li]', $stats) . '[/li][/ul]';
     }
     /****************/
     /* Main Content */
     /****************/
     // get spawns and path
     $map = null;
     if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL)) {
         $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns];
         foreach ($spawns as $areaId => &$areaData) {
             $map['extra'][$areaId] = ZoneList::getName($areaId);
         }
     }
     // consider pooled spawns
     $this->map = $map;
     $this->infobox = '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]';
     $this->placeholder = $placeholder;
     $this->accessory = $accessory;
     $this->quotes = $this->getQuotes();
     $this->reputation = $this->getOnKillRep($_altIds, $mapType);
     $this->redButtons = array(BUTTON_WOWHEAD => true, BUTTON_LINKS => true, BUTTON_VIEW3D => ['type' => TYPE_NPC, 'typeId' => $this->typeId, 'displayId' => $this->subject->getRandomModelId()]);
     /**************/
     /* Extra Tabs */
     /**************/
     // tab: SAI
     // hmm, how should this look like
     // tab: abilities / tab_controlledabilities (dep: VehicleId)
     // SMART_SCRIPT_TYPE_CREATURE = 0; SMART_ACTION_CAST = 11; SMART_ACTION_ADD_AURA = 75; SMART_ACTION_INVOKER_CAST = 85; SMART_ACTION_CROSS_CAST = 86
     $smartSpells = DB::World()->selectCol('SELECT action_param1 FROM smart_scripts WHERE source_type = 0 AND action_type IN (11, 75, 85, 86) AND entryOrGUID = ?d', $this->typeId);
     $tplSpells = [];
     $conditions = ['OR'];
     for ($i = 1; $i < 9; $i++) {
         if ($_ = $this->subject->getField('spell' . $i)) {
             $tplSpells[] = $_;
         }
     }
     if ($tplSpells) {
         $conditions[] = ['id', $tplSpells];
     }
     if ($smartSpells) {
         $conditions[] = ['id', $smartSpells];
     }
     // Pet-Abilities
     if ($_typeFlags & 0x1 && ($_ = $this->subject->getField('family'))) {
         $skill = 0;
         $mask = 0x0;
         foreach (Util::$skillLineMask[-1] as $idx => $pair) {
             if ($pair[0] != $_) {
                 continue;
             }
             $skill = $pair[1];
             $mask = 1 << $idx;
             break;
         }
         $conditions[] = ['AND', ['s.typeCat', -3], ['OR', ['skillLine1', $skill], ['AND', ['skillLine1', 0, '>'], ['skillLine2OrMask', $skill]], ['AND', ['skillLine1', -1], ['skillLine2OrMask', $mask, '&']]]];
     }
     if (count($conditions) > 1) {
         $abilities = new SpellList($conditions);
         if (!$abilities->error) {
             $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
             $controled = $abilities->getListviewData();
             $normal = [];
             foreach ($controled as $id => $values) {
                 if (in_array($id, $smartSpells)) {
                     $normal[$id] = $values;
                     unset($controled[$id]);
                     continue;
                 }
                 // not quite right. All seats should be checked for allowed-to-cast-flag-something
                 if (!$this->subject->getField('vehicleId') && in_array($id, $tplSpells)) {
                     $normal[$id] = $values;
                     unset($controled[$id]);
                 }
             }
             if ($normal) {
                 $this->lvTabs[] = array('file' => 'spell', 'data' => $normal, 'params' => array('name' => '$LANG.tab_abilities', 'id' => 'abilities'));
             }
             if ($controled) {
                 $this->lvTabs[] = array('file' => 'spell', 'data' => $controled, 'params' => array('name' => '$LANG.tab_controlledabilities', 'id' => 'controlled-abilities'));
             }
         }
     }
     // tab: summoned by
     $conditions = array('OR', ['AND', ['effect1Id', 28], ['effect1MiscValue', $this->typeId]], ['AND', ['effect2Id', 28], ['effect2MiscValue', $this->typeId]], ['AND', ['effect3Id', 28], ['effect3MiscValue', $this->typeId]]);
     $summoned = new SpellList($conditions);
     if (!$summoned->error) {
         $this->extendGlobalData($summoned->getJSGlobals());
         $this->lvTabs[] = array('file' => 'spell', 'data' => $summoned->getListviewData(), 'params' => array('name' => '$LANG.tab_summonedby', 'id' => 'summoned-by'));
     }
     // tab: teaches
     if ($this->subject->getField('npcflag') & NPC_FLAG_TRAINER) {
         $teachQuery = '
             SELECT    IFNULL(t2.SpellID, t1.SpellID) AS ARRAY_KEY,
                       IFNULL(t2.MoneyCost, t1.MoneyCost) AS cost,
                       IFNULL(t2.ReqSkillLine, t1.ReqSkillLine) AS reqSkillId,
                       IFNULL(t2.ReqSkillRank, t1.ReqSkillRank) AS reqSkillValue,
                       IFNULL(t2.ReqLevel, t1.ReqLevel) AS reqLevel
             FROM      npc_trainer t1
             LEFT JOIN npc_trainer t2 ON t2.ID = IF(t1.SpellID < 0, -t1.SpellID, null)
             WHERE     t1.ID = ?d
         ';
         if ($tSpells = DB::World()->select($teachQuery, $this->typeId)) {
             $teaches = new SpellList(array(['id', array_keys($tSpells)]));
             if (!$teaches->error) {
                 $this->extendGlobalData($teaches->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
                 $data = $teaches->getListviewData();
                 $extra = [];
                 foreach ($tSpells as $sId => $train) {
                     if (empty($data[$sId])) {
                         continue;
                     }
                     if ($_ = $train['reqSkillId']) {
                         $this->extendGlobalIds(TYPE_SKILL, $_);
                         if (!isset($extra[0])) {
                             $extra[0] = 'Listview.extraCols.condition';
                         }
                         $data[$sId]['condition'][0][$this->typeId][] = [[CND_SKILL, $_, $train['reqSkillValue']]];
                     }
                     if ($_ = $train['reqLevel']) {
                         if (!isset($extra[1])) {
                             $extra[1] = "Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')";
                         }
                         $data[$sId]['reqLevel'] = $_;
                     }
                     if ($_ = $train['cost']) {
                         $data[$sId]['trainingcost'] = $_;
                     }
                 }
                 $this->lvTabs[] = array('file' => 'spell', 'data' => $data, 'params' => array('name' => '$LANG.tab_teaches', 'id' => 'teaches', 'visibleCols' => "\$['trainingcost']", 'extraCols' => $extra ? '$[' . implode(', ', $extra) . ']' : null));
             }
         } else {
             trigger_error('NPC ' . $this->typeId . ' is flagged as trainer, but doesn\'t have any spells set', E_USER_WARNING);
         }
     }
     // tab: sells
     if ($sells = DB::World()->selectCol('SELECT item FROM npc_vendor nv WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor genv JOIN creature c ON genv.guid = c.guid WHERE c.id = ?d', $this->typeId, $this->typeId)) {
         $soldItems = new ItemList(array(['id', $sells]));
         if (!$soldItems->error) {
             $extraCols = ["Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost'];
             if ($soldItems->hasSetFields(['condition'])) {
                 $extraCols[] = 'Listview.extraCols.condition';
             }
             $lvData = $soldItems->getListviewData(ITEMINFO_VENDOR, [TYPE_NPC => [$this->typeId]]);
             $sc = Util::getServerConditions(CND_SRC_NPC_VENDOR, $this->typeId);
             if (!empty($sc[0])) {
                 $this->extendGlobalData($sc[1]);
                 if (!array_search('Listview.extraCols.condition', $extraCols)) {
                     $extraCols[] = 'Listview.extraCols.condition';
                 }
                 foreach ($lvData as $id => &$row) {
                     foreach ($sc[0] as $srcType => $cndData) {
                         if (!empty($cndData[$id . ':' . $this->typeId])) {
                             $row['condition'][0][$id . ':' . $this->typeId] = $cndData[$id . ':' . $this->typeId];
                         }
                     }
                 }
             }
             $this->lvTabs[] = array('file' => 'item', 'data' => $lvData, 'params' => array('name' => '$LANG.tab_sells', 'id' => 'currency-for', 'extraCols' => '$[' . implode(', ', $extraCols) . ']'));
             $this->extendGlobalData($soldItems->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
         }
     }
     // tabs: this creature contains..
     $skinTab = ['tab_skinning', 'skinning'];
     if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT) {
         $skinTab = ['tab_herbalism', 'herbalism'];
     } else {
         if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT) {
             $skinTab = ['tab_mining', 'mining'];
         } else {
             if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT) {
                 $skinTab = ['tab_engineering', 'engineering'];
             }
         }
     }
     /*
             extraCols: [Listview.extraCols.count, Listview.extraCols.percent, Listview.extraCols.mode],
             _totalCount: 22531,
             computeDataFunc: Listview.funcBox.initLootTable,
             onAfterCreate: Listview.funcBox.addModeIndicator,
     
             modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}}
     */
     $sourceFor = array([LOOT_CREATURE, $this->subject->getField('lootId'), '$LANG.tab_drops', 'drops', []], [LOOT_PICKPOCKET, $this->subject->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['side', 'slot', 'reqlevel']], [LOOT_SKINNING, $this->subject->getField('skinLootId'), '$LANG.' . $skinTab[0], $skinTab[1], ['side', 'slot', 'reqlevel']]);
     // temp: manually add loot for difficulty-versions
     $langref = array("-2" => '$LANG.tab_heroic', "-1" => '$LANG.tab_normal', 1 => '$$WH.sprintf(LANG.tab_normalX, 10)', 2 => '$$WH.sprintf(LANG.tab_normalX, 25)', 3 => '$$WH.sprintf(LANG.tab_heroicX, 10)', 4 => '$$WH.sprintf(LANG.tab_heroicX, 25)');
     if ($_altIds) {
         $sourceFor[0][2] = $mapType == 1 ? $langref[-1] : $langref[1];
         foreach ($_altNPCs->iterate() as $id => $__) {
             $mode = ($_altIds[$id] + 1) * ($mapType == 1 ? -1 : 1);
             if ($lootGO = DB::Aowow()->selectRow('SELECT o.id, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE l.npcId = ?d', $id)) {
                 array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lootGO['lootId'], $langref[$mode], 'drops-object-' . abs($mode), [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, ' . $lootGO['id'] . ', \'' . Util::jsEscape(Util::localizedString($lootGO, 'name')) . '\')']]);
             }
             if ($lootId = $_altNPCs->getField('lootId')) {
                 array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $lootId, $langref[$mode], 'drops-' . abs($mode), []]]);
             }
         }
     }
     if ($lootGOs = DB::Aowow()->select('SELECT o.id, IF(npcId < 0, 1, 0) AS modeDummy, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE ABS(l.npcId) = ?d', $this->typeId)) {
         foreach ($lootGOs as $idx => $lgo) {
             array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lgo['lootId'], $mapType ? $langref[($mapType == 1 ? -1 : 1) + ($lgo['modeDummy'] ? 1 : 0)] : '$LANG.tab_drops', 'drops-object-' . $idx, [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, ' . $lgo['id'] . ', \'' . Util::jsEscape(Util::localizedString($lgo, 'name')) . '\')']]);
         }
     }
     $reqQuest = [];
     foreach ($sourceFor as $sf) {
         $creatureLoot = new Loot();
         if ($creatureLoot->getByContainer($sf[0], $sf[1])) {
             $extraCols = $creatureLoot->extraCols;
             $extraCols[] = 'Listview.extraCols.percent';
             $this->extendGlobalData($creatureLoot->jsGlobals);
             foreach ($creatureLoot->iterate() as &$lv) {
                 if (!$lv['quest']) {
                     continue;
                 }
                 $extraCols[] = 'Listview.extraCols.condition';
                 $reqQuest[$lv['id']] = 0;
                 $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
             }
             $lootTab = array('file' => 'item', 'data' => $creatureLoot->getResult(), 'params' => array('name' => $sf[2], 'id' => $sf[3], 'extraCols' => "\$[" . implode(', ', array_unique($extraCols)) . "]", 'hiddenCols' => $sf[4] ? "\$" . Util::toJSON($sf[4]) : null, 'sort' => "\$['-percent', 'name']"));
             if (!empty($sf['note'])) {
                 $lootTab['params']['note'] = $sf['note'];
             }
             $this->lvTabs[] = $lootTab;
         }
     }
     if ($reqIds = array_keys($reqQuest)) {
         $conditions = array('OR', ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds], ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds], ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds], ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds]);
         $reqQuests = new QuestList($conditions);
         $this->extendGlobalData($reqQuests->getJSGlobals());
         foreach ($reqQuests->iterate() as $qId => $__) {
             if (empty($reqQuests->requires[$qId][TYPE_ITEM])) {
                 continue;
             }
             foreach ($reqIds as $rId) {
                 if (in_array($rId, $reqQuests->requires[$qId][TYPE_ITEM])) {
                     $reqQuest[$rId] = $reqQuests->id;
                 }
             }
         }
     }
     // tab: starts quest
     // tab: ends quest
     $startEnd = new QuestList(array(['qse.type', TYPE_NPC], ['qse.typeId', $this->typeId]));
     if (!$startEnd->error) {
         $this->extendGlobalData($startEnd->getJSGlobals());
         $lvData = $startEnd->getListviewData();
         $_ = [[], []];
         foreach ($startEnd->iterate() as $id => $__) {
             $m = $startEnd->getField('method');
             if ($m & 0x1) {
                 $_[0][] = $lvData[$id];
             }
             if ($m & 0x2) {
                 $_[1][] = $lvData[$id];
             }
         }
         if ($_[0]) {
             $this->lvTabs[] = array('file' => 'quest', 'data' => $_[0], 'params' => array('name' => '$LANG.tab_starts', 'id' => 'starts'));
         }
         if ($_[1]) {
             $this->lvTabs[] = array('file' => 'quest', 'data' => $_[1], 'params' => array('name' => '$LANG.tab_ends', 'id' => 'ends'));
         }
     }
     // tab: objective of quest
     $conditions = array('OR', ['AND', ['reqNpcOrGo1', $this->typeId], ['reqNpcOrGoCount1', 0, '>']], ['AND', ['reqNpcOrGo2', $this->typeId], ['reqNpcOrGoCount2', 0, '>']], ['AND', ['reqNpcOrGo3', $this->typeId], ['reqNpcOrGoCount3', 0, '>']], ['AND', ['reqNpcOrGo4', $this->typeId], ['reqNpcOrGoCount4', 0, '>']]);
     $objectiveOf = new QuestList($conditions);
     if (!$objectiveOf->error) {
         $this->extendGlobalData($objectiveOf->getJSGlobals());
         $this->lvTabs[] = array('file' => 'quest', 'data' => $objectiveOf->getListviewData(), 'params' => array('name' => '$LANG.tab_objectiveof', 'id' => 'objective-of'));
     }
     // tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for]
     $conditions = array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE]], ['ac.value1', $this->typeId]);
     $crtOf = new AchievementList($conditions);
     if (!$crtOf->error) {
         $this->extendGlobalData($crtOf->getJSGlobals());
         $this->lvTabs[] = array('file' => 'achievement', 'data' => $crtOf->getListviewData(), 'params' => array('name' => '$LANG.tab_criteriaof', 'id' => 'criteria-of'));
     }
     // tab: passengers
     if ($_ = DB::World()->selectCol('SELECT accessory_entry AS ARRAY_KEY, GROUP_CONCAT(seat_id) FROM vehicle_template_accessory WHERE entry = ?d GROUP BY accessory_entry', $this->typeId)) {
         $passengers = new CreatureList(array(['id', array_keys($_)]));
         if (!$passengers->error) {
             $data = $passengers->getListviewData();
             $xCols = null;
             if (User::isInGroup(U_GROUP_STAFF)) {
                 foreach ($data as $id => &$d) {
                     $d['seat'] = str_replace(',', ', ', $_[$id]);
                 }
                 $xCols = "\$[Listview.funcBox.createSimpleCol('seat', '" . Lang::npc('seat') . "', '10%', 'seat')]";
             }
             $this->extendGlobalData($passengers->getJSGlobals(GLOBALINFO_SELF));
             $this->lvTabs[] = array('file' => 'creature', 'data' => $data, 'params' => array('extraCols' => $xCols, 'name' => Lang::npc('accessory'), 'id' => 'accessory'));
         }
     }
 }
コード例 #2
0
ファイル: items.php プロジェクト: TrinityCore/aowow
 protected function generateContent()
 {
     $this->addJS('?data=weight-presets&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']);
     $conditions = [];
     if (!User::isInGroup(U_GROUP_EMPLOYEE)) {
         $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
     }
     /*******************/
     /* evaluate filter */
     /*******************/
     // recreate form selection (must be evaluated first via getConditions())
     if ($_ = $this->filterObj->getConditions()) {
         $conditions[] = $_;
     }
     $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter);
     $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
     $this->filter['fi'] = $this->filterObj->getForm();
     $menu = $this->createExtraMenus();
     foreach ($menu['type'][0] as $k => $str) {
         if ($str && (!$menu['type'][1] || $menu['type'][1] & 1 << $k)) {
             $this->filter['type'][$k] = $str;
         }
     }
     foreach ($menu['slot'][0] as $k => $str) {
         if ($str && (!$menu['slot'][1] || $menu['slot'][1] & 1 << $k)) {
             $this->filter['slot'][$k] = $str;
         }
     }
     if (isset($this->filter['slot'][INVTYPE_SHIELD])) {
         // "Off Hand" => "Shield"
         $this->filter['slot'][INVTYPE_SHIELD] = Lang::item('armorSubClass', 6);
     }
     $xCols = $this->filterObj->getForm('extraCols', true);
     $infoMask = ITEMINFO_JSON;
     if (array_intersect([63, 64, 125], $xCols)) {
         // 63:buyPrice; 64:sellPrice; 125:reqarenartng
         $infoMask |= ITEMINFO_VENDOR;
     }
     if (!empty($this->filter['fi']['extraCols'])) {
         $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, ' . (isset($this->filter['gm']) ? $this->filter['gm'] : 0) . ', ' . (array_intersect([63], $xCols) ? 1 : 0) . ')';
     }
     if ($this->filterObj->error) {
         $this->sharedLV['_errors'] = '$1';
     }
     /******************/
     /* set conditions */
     /******************/
     if (isset($this->category[0])) {
         $conditions[] = ['i.class', $this->category[0]];
     }
     if (isset($this->category[1])) {
         $conditions[] = ['i.subClass', $this->category[1]];
     }
     if (isset($this->category[2])) {
         $conditions[] = ['i.subSubClass', $this->category[2]];
     }
     /***********************/
     /* handle auto-gemming */
     /***********************/
     $this->gemScores = $this->createGemScores();
     /*************************/
     /* handle upgrade search */
     /*************************/
     $upgItemData = [];
     if (!empty($this->filter['upg']) && !empty($this->filter['fi']['setWeights'])) {
         $upgItems = new ItemList(array(['id', array_keys($this->filter['upg'])]), ['extraOpts' => $this->filterObj->extraOpts]);
         if (!$upgItems->error) {
             $this->extendGlobalData($upgItems->getJSGlobals());
             $upgItemData = $upgItems->getListviewData($infoMask);
         }
     }
     if ($upgItemData) {
         $singleSlot = true;
         $ref = reset($this->filter['upg']);
         foreach ($this->filter['upg'] as $slot) {
             if ($slot == $ref) {
                 continue;
             }
             $singleSlot = false;
             break;
         }
         if ($singleSlot && empty($this->filter['gb'])) {
             // enforce group by slot
             $this->filter['gb'] = 1;
         } else {
             if (!$singleSlot) {
                 $this->filter['gb'] = 1;
                 $maxResults = 25;
                 $this->sharedLV['customFilter'] = '$fi_filterUpgradeListview';
             }
         }
     }
     /********************************************************************************************************************************/
     /* group by                                                                                                                     */
     /*                                                                                                                              */
     /* cases that make sense:                                                                                                       */
     /* no upgItems             -> everything goes                                                                                   */
     /*  1 upgItems             OR                                                                                                   */
     /*  N upgItems (same slot) -> gb:none   - disabled                                                                              */
     /*                         -> gb:slot   - limited to slot of the upgItems (in theory weapons create a tab for each weapon type) */
     /*                         -> gb:level  - upgItems is added to all tabs                                                         */
     /*                         -> gb:source - upgItems is added to all tabs                                                         */
     /*  N upgItems (random)    -> gb:none   - disabled                                                                              */
     /*                         -> gb:slot   - only slots existing within the upgItems; match upgItems to slot                       */
     /*                         -> gb:level  - disabled                                                                              */
     /*                         -> gb:source - disabled                                                                              */
     /********************************************************************************************************************************/
     $availableSlots = array(ITEM_CLASS_ARMOR => [INVTYPE_HEAD, INVTYPE_NECK, INVTYPE_SHOULDERS, INVTYPE_CHEST, INVTYPE_WAIST, INVTYPE_LEGS, INVTYPE_FEET, INVTYPE_WRISTS, INVTYPE_HANDS, INVTYPE_FINGER, INVTYPE_TRINKET, INVTYPE_SHIELD, INVTYPE_CLOAK], ITEM_CLASS_WEAPON => [INVTYPE_WEAPON, INVTYPE_RANGED, INVTYPE_2HWEAPON, INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPONOFFHAND, INVTYPE_THROWN, INVTYPE_HOLDABLE]);
     $groups = [];
     $nameSource = [];
     $grouping = isset($this->filter['gb']) ? $this->filter['gb'] : null;
     $extraOpts = [];
     $maxResults = CFG_SQL_LIMIT_DEFAULT;
     switch ($grouping) {
         // slot: (try to limit the lookups by class grouping and intersecting with preselected slots)
         // if intersect yields an empty array no lookups will occur
         case 1:
             if (isset($this->category[0]) && $this->category[0] == ITEM_CLASS_ARMOR) {
                 $groups = $availableSlots[ITEM_CLASS_ARMOR];
             } else {
                 if (isset($this->category[0]) && $this->category[0] == ITEM_CLASS_WEAPON) {
                     $groups = $availableSlots[ITEM_CLASS_WEAPON];
                 } else {
                     $groups = array_merge($availableSlots[ITEM_CLASS_ARMOR], $availableSlots[ITEM_CLASS_WEAPON]);
                 }
             }
             if (isset($this->filter['sl'])) {
                 // skip lookups for unselected slots
                 $groups = array_intersect($groups, (array) $this->filter['sl']);
             }
             if (!empty($this->filter['upg'])) {
                 // skip lookups for slots we dont have items to upgrade for
                 $groups = array_intersect($groups, (array) $this->filter['upg']);
             }
             if ($groups) {
                 $nameSource = Lang::item('inventoryType');
                 $this->forceTabs = true;
             }
             break;
         case 2:
             // itemlevel: first, try to find 10 level steps within range (if given) as tabs
             // ohkayy, maybe i need to rethink $this
             $this->filterOpts = $this->filterObj->extraOpts;
             $this->filterOpts['is']['o'] = [null];
             // remove 'order by' from itemStats
             $extraOpts = array_merge($this->filterOpts, ['i' => ['g' => ['itemlevel'], 'o' => ['itemlevel DESC']]]);
             $levelRef = new ItemList(array_merge($conditions, [10]), ['extraOpts' => $extraOpts]);
             foreach ($levelRef->iterate() as $_) {
                 $l = $levelRef->getField('itemLevel');
                 $groups[] = $l;
                 $nameSource[$l] = Lang::game('level') . ' ' . $l;
             }
             if ($groups) {
                 $l = -end($groups);
                 $groups[] = $l;
                 // push last value as negativ to signal misc group after $this level
                 $extraOpts = ['i' => ['o' => ['itemlevel DESC']]];
                 $nameSource[$l] = Lang::item('tabOther');
                 $this->forceTabs = true;
             }
             break;
         case 3:
             // source
             $groups = [1, 2, 3, 4, 5, 10, 11, 12, 0];
             $nameSource = Lang::game('sources');
             $this->forceTabs = true;
             break;
             // none
         // none
         default:
             $grouping = 0;
             $groups[0] = null;
     }
     /*****************************/
     /* create lv-tabs for groups */
     /*****************************/
     foreach ($groups as $group) {
         switch ($grouping) {
             case 1:
                 $finalCnd = array_merge($conditions, [['slot', $group], $maxResults]);
                 break;
             case 2:
                 $finalCnd = array_merge($conditions, [['itemlevel', abs($group), $group > 0 ? null : '<'], $maxResults]);
                 break;
             case 3:
                 $finalCnd = array_merge($conditions, [$group ? ['src.src' . $group, null, '!'] : ['src.typeId', null], $maxResults]);
                 break;
             default:
                 $finalCnd = $conditions;
         }
         $items = new ItemList($finalCnd, ['extraOpts' => array_merge($extraOpts, $this->filterObj->extraOpts)]);
         if ($items->error) {
             continue;
         }
         $this->extendGlobalData($items->getJSGlobals());
         $tabData = array_merge(['data' => $items->getListviewData($infoMask)], $this->sharedLV);
         $upg = [];
         if ($upgItemData) {
             if ($grouping == 1) {
                 $upg = array_keys(array_filter($this->filter['upg'], function ($v) use($group) {
                     return $v == $group;
                 }));
                 foreach ($upg as $uId) {
                     $tabData['data'][$uId] = $upgItemData[$uId];
                 }
                 if ($upg) {
                     $tabData['_upgradeIds'] = $upg;
                 }
             } else {
                 if ($grouping) {
                     $upg = array_keys($this->filter['upg']);
                     $tabData['_upgradeIds'] = $upg;
                     foreach ($upgItemData as $uId => $data) {
                         // using numeric keys => cant use array_merge
                         $tabData['data'][$uId] = $data;
                     }
                 }
             }
         }
         if ($grouping) {
             switch ($grouping) {
                 case 1:
                     $tabData['id'] = 'slot-' . $group;
                     break;
                 case 2:
                     $tabData['id'] = $group > 0 ? 'level-' . $group : 'other';
                     break;
                 case 3:
                     $tabData['id'] = $group ? 'source-' . $group : 'unknown';
                     break;
             }
             $tabData['name'] = $nameSource[$group];
             $tabData['tabs'] = '$tabsGroups';
         }
         if (!empty($this->filter['fi']['setWeights'])) {
             if ($items->hasSetFields(['armor'])) {
                 $tabData['visibleCols'][] = 'armor';
             }
         }
         // create note if search limit was exceeded; overwriting 'note' is intentional
         if ($items->getMatches() > $maxResults && count($groups) > 1) {
             $tabData['_truncated'] = 1;
             $cls = isset($this->category[0]) ? '=' . $this->category[0] : '';
             $override = ['gb' => ''];
             if ($upg) {
                 $override['upg'] = implode(':', $upg);
             }
             switch ($grouping) {
                 case 1:
                     $override['sl'] = $group;
                     $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \'' . $cls . '\', \'' . $this->filterObj->urlize($override) . '\')';
                     break;
                 case 2:
                     if ($group > 0) {
                         $override['minle'] = $group;
                         $override['maxle'] = $group;
                     } else {
                         $override['maxle'] = abs($group) - 1;
                     }
                     $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \'' . $cls . '\', \'' . $this->filterObj->urlize($override) . '\')';
                     break;
                 case 3:
                     if ($_ = [null, 3, 4, 5, 6, 7, 9, 10, 11][$group]) {
                         $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \'' . $cls . '\', \'' . $this->filterObj->urlize($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]) . '\')';
                     }
                     break;
             }
         } else {
             if ($items->getMatches() > $maxResults) {
                 $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsfound', $items->getMatches(), CFG_SQL_LIMIT_DEFAULT);
                 $tabData['_truncated'] = 1;
             }
         }
         foreach ($tabData as $k => $p) {
             if (!$p && $k != 'data') {
                 unset($tabData[$k]);
             }
         }
         if ($grouping) {
             $tabData['hideCount'] = 1;
         }
         $tabData['data'] = array_values($tabData['data']);
         $this->lvTabs[] = ['item', $tabData];
     }
     // reformat for use in template
     if (!empty($this->filter['upg'])) {
         $this->filter['upg'] = implode(':', array_keys($this->filter['upg']));
     }
     // whoops, we have no data? create emergency content
     if (empty($this->lvTabs)) {
         $this->forceTabs = false;
         $this->lvTabs[] = ['item', ['data' => []]];
     }
     // sort for dropdown-menus
     Lang::sort('game', 'ra');
     Lang::sort('game', 'cl');
 }
コード例 #3
0
ファイル: item.php プロジェクト: Carbenium/aowow
 protected function generateContent()
 {
     $this->addJS('?data=weight-presets.zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']);
     $_flags = $this->subject->getField('flags');
     $_slot = $this->subject->getField('slot');
     $_class = $this->subject->getField('class');
     $_subClass = $this->subject->getField('subClass');
     $_bagFamily = $this->subject->getField('bagFamily');
     $_model = $this->subject->getField('displayId');
     $_visSlots = array(INVTYPE_HEAD, INVTYPE_SHOULDERS, INVTYPE_BODY, INVTYPE_CHEST, INVTYPE_WAIST, INVTYPE_LEGS, INVTYPE_FEET, INVTYPE_WRISTS, INVTYPE_HANDS, INVTYPE_WEAPON, INVTYPE_SHIELD, INVTYPE_RANGED, INVTYPE_CLOAK, INVTYPE_2HWEAPON, INVTYPE_TABARD, INVTYPE_ROBE, INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPONOFFHAND, INVTYPE_HOLDABLE, INVTYPE_THROWN, INVTYPE_RANGEDRIGHT);
     /***********/
     /* Infobox */
     /***********/
     $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
     // itemlevel
     if (in_array($_class, [ITEM_CLASS_ARMOR, ITEM_CLASS_WEAPON, ITEM_CLASS_AMMUNITION]) || $this->subject->getField('gemEnchantmentId')) {
         $infobox[] = Lang::game('level') . Lang::main('colon') . $this->subject->getField('itemLevel');
     }
     // account-wide
     if ($_flags & ITEM_FLAG_ACCOUNTBOUND) {
         $infobox[] = Lang::item('accountWide');
     }
     // side
     if ($si = $this->subject->json[$this->typeId]['side']) {
         if ($si != 3) {
             $infobox[] = Lang::main('side') . Lang::main('colon') . '[span class=icon-' . ($si == 1 ? 'alliance' : 'horde') . ']' . Lang::game('si', $si) . '[/span]';
         }
     }
     // consumable / not consumable
     if (!$_slot) {
         $hasUse = false;
         for ($i = 1; $i < 6; $i++) {
             if ($this->subject->getField('spellId' . $i) <= 0 || in_array($this->subject->getField('spellTrigger' . $i), [1, 2])) {
                 continue;
             }
             $hasUse = true;
             if ($this->subject->getField('spellCharges' . $i) >= 0) {
                 continue;
             }
             $tt = '[tooltip=tooltip_consumedonuse]' . Lang::item('consumable') . '[/tooltip]';
             break;
         }
         if ($hasUse) {
             $infobox[] = isset($tt) ? $tt : '[tooltip=tooltip_notconsumedonuse]' . Lang::item('nonConsumable') . '[/tooltip]';
         }
     }
     // related holiday
     if ($hId = $this->subject->getField('holidayId')) {
         if ($hName = DB::Aowow()->selectRow('SELECT * FROM ?_holidays WHERE id = ?d', $hId)) {
             $infobox[] = Lang::game('eventShort') . Lang::main('colon') . '[url=?event=' . $hId . ']' . Util::localizedString($hName, 'name') . '[/url]';
         }
     }
     // tool
     if ($tId = $this->subject->getField('totemCategory')) {
         if ($tName = DB::Aowow()->selectRow('SELECT * FROM ?_totemcategory WHERE id = ?d', $tId)) {
             $infobox[] = Lang::item('tool') . Lang::main('colon') . '[url=?items&filter=cr=91;crs=' . $tId . ';crv=0]' . Util::localizedString($tName, 'name') . '[/url]';
         }
     }
     // extendedCost
     if (!empty($this->subject->getExtendedCost([], $_reqRating)[$this->subject->id])) {
         $vendors = $this->subject->getExtendedCost()[$this->subject->id];
         $each = $this->subject->getField('stackable') > 1 ? '[color=q0] (' . Lang::item('each') . ')[/color]' : null;
         $handled = [];
         $costList = [];
         foreach ($vendors as $npcId => $data) {
             $tokens = [];
             $currency = [];
             if (!is_array($data)) {
                 continue;
             }
             foreach ($data as $c => $qty) {
                 if (is_string($c)) {
                     unset($data[$c]);
                     // unset miscData to prevent having two vendors /w the same cost being cached, because of different stock or rating-requirements
                     continue;
                 }
                 if ($c < 0) {
                     // currency items (and honor or arena)
                     $currency[] = -$c . ',' . $qty;
                 } else {
                     if ($c > 0) {
                         // plain items (item1,count1,item2,count2,...)
                         $tokens[$c] = $c . ',' . $qty;
                     }
                 }
             }
             // display every cost-combination only once
             if (in_array(md5(serialize($data)), $handled)) {
                 continue;
             }
             $handled[] = md5(serialize($data));
             $cost = isset($data[0]) ? '[money=' . $data[0] : '[money';
             if ($tokens) {
                 $cost .= ' items=' . implode(',', $tokens);
             }
             if ($currency) {
                 $cost .= ' currency=' . implode(',', $currency);
             }
             $cost .= ']';
             $costList[] = $cost;
         }
         if (count($costList) == 1) {
             $infobox[] = Lang::item('cost') . Lang::main('colon') . $costList[0] . $each;
         } else {
             if (count($costList) > 1) {
                 $infobox[] = Lang::item('cost') . $each . Lang::main('colon') . '[ul][li]' . implode('[/li][li]', $costList) . '[/li][/ul]';
             }
         }
         if ($_reqRating) {
             $res = [];
             $i = 0;
             $len = 0;
             $parts = explode(' ', str_replace('<br>', ' ', sprintf(Lang::item('reqRating', $_reqRating[1]), $_reqRating[0])));
             foreach ($parts as $p) {
                 $res[$i][] = $p;
                 $len += mb_strlen($p) + 1;
                 if ($len < 30) {
                     continue;
                 }
                 $len = 0;
                 $i++;
             }
             foreach ($res as &$r) {
                 $r = implode(' ', $r);
             }
             $infobox[] = implode('[br]', $res);
         }
     }
     // repair cost
     if ($_ = $this->subject->getField('repairPrice')) {
         $infobox[] = Lang::item('repairCost') . Lang::main('colon') . '[money=' . $_ . ']';
     }
     // avg auction buyout
     if (in_array($this->subject->getField('bonding'), [0, 2, 3])) {
         if ($_ = Util::getBuyoutForItem($this->typeId)) {
             $infobox[] = '[tooltip=tooltip_buyoutprice]' . Lang::item('buyout.') . '[/tooltip]' . Lang::main('colon') . '[money=' . $_ . ']' . $each;
         }
     }
     // avg money contained
     if ($_flags & ITEM_FLAG_OPENABLE) {
         if ($_ = intVal(($this->subject->getField('minMoneyLoot') + $this->subject->getField('maxMoneyLoot')) / 2)) {
             $infobox[] = Lang::item('worth') . Lang::main('colon') . '[tooltip=tooltip_avgmoneycontained][money=' . $_ . '][/tooltip]';
         }
     }
     // if it goes into a slot it may be disenchanted
     if ($_slot && $_class != ITEM_CLASS_CONTAINER) {
         if ($this->subject->getField('disenchantId')) {
             $_ = $this->subject->getField('requiredDisenchantSkill');
             if ($_ < 1) {
                 // these are some items, that never went live .. extremely rough emulation here
                 $_ = intVal($this->subject->getField('itemLevel') / 7.5) * 25;
             }
             $infobox[] = Lang::item('disenchantable') . '&nbsp;([tooltip=tooltip_reqenchanting]' . $_ . '[/tooltip])';
         } else {
             $infobox[] = Lang::item('cantDisenchant');
         }
     }
     if ($_flags & ITEM_FLAG_MILLABLE && $this->subject->getField('requiredSkill') == 773) {
         $infobox[] = Lang::item('millable') . '&nbsp;([tooltip=tooltip_reqinscription]' . $this->subject->getField('requiredSkillRank') . '[/tooltip])';
     }
     if ($_flags & ITEM_FLAG_PROSPECTABLE && $this->subject->getField('requiredSkill') == 755) {
         $infobox[] = Lang::item('prospectable') . '&nbsp;([tooltip=tooltip_reqjewelcrafting]' . $this->subject->getField('requiredSkillRank') . '[/tooltip])';
     }
     if ($_flags & ITEM_FLAG_DEPRECATED) {
         $infobox[] = '[tooltip=tooltip_deprecated]' . Lang::item('deprecated') . '[/tooltip]';
     }
     if ($_flags & ITEM_FLAG_NO_EQUIPCD) {
         $infobox[] = '[tooltip=tooltip_noequipcooldown]' . Lang::item('noEquipCD') . '[/tooltip]';
     }
     if ($_flags & ITEM_FLAG_PARTYLOOT) {
         $infobox[] = '[tooltip=tooltip_partyloot]' . Lang::item('partyLoot') . '[/tooltip]';
     }
     if ($_flags & ITEM_FLAG_REFUNDABLE) {
         $infobox[] = '[tooltip=tooltip_refundable]' . Lang::item('refundable') . '[/tooltip]';
     }
     if ($_flags & ITEM_FLAG_SMARTLOOT) {
         $infobox[] = '[tooltip=tooltip_smartloot]' . Lang::item('smartLoot') . '[/tooltip]';
     }
     if ($_flags & ITEM_FLAG_INDESTRUCTIBLE) {
         $infobox[] = Lang::item('indestructible');
     }
     if ($_flags & ITEM_FLAG_USABLE_ARENA) {
         $infobox[] = Lang::item('useInArena');
     }
     if ($_flags & ITEM_FLAG_USABLE_SHAPED) {
         $infobox[] = Lang::item('useInShape');
     }
     // cant roll need
     if ($this->subject->getField('flagsExtra') & 0x100) {
         $infobox[] = '[tooltip=tooltip_cannotrollneed]' . Lang::item('noNeedRoll') . '[/tooltip]';
     }
     // fits into keyring
     if ($_bagFamily & 0x100) {
         $infobox[] = Lang::item('atKeyring');
     }
     /****************/
     /* Main Content */
     /****************/
     $_cu = in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]) || $this->subject->getField('gemEnchantmentId');
     $this->headIcons = [$this->subject->getField('iconString'), $this->subject->getField('stackable')];
     $this->infobox = $infobox ? '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]' : null;
     $this->tooltip = $this->subject->renderTooltip(true);
     $this->redButtons = array(BUTTON_WOWHEAD => true, BUTTON_LINKS => ['color' => 'ff' . Util::$rarityColorStings[$this->subject->getField('quality')], 'linkId' => 'item:' . $this->typeId . ':0:0:0:0:0:0:0:0'], BUTTON_VIEW3D => in_array($_slot, $_visSlots) && $_model ? ['displayId' => $this->subject->getField('displayId'), 'slot' => $_slot, 'type' => TYPE_ITEM, 'typeId' => $this->typeId] : false, BUTTON_COMPARE => $_cu, BUTTON_EQUIP => in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]), BUTTON_UPGRADE => $_cu ? ['class' => $_class, 'slot' => $_slot] : false);
     // availablility
     $this->disabled = false;
     // todo (med): get itemSources (which are not yet in DB :x) or
     // pageText
     if ($next = $this->subject->getField('pageTextId')) {
         $this->addJS('Book.js');
         $this->addCSS(['path' => 'Book.css']);
         while ($next) {
             $row = DB::World()->selectRow('SELECT *, text as Text_loc0 FROM page_text pt LEFT JOIN locales_page_text lpt ON pt.entry = lpt.entry WHERE pt.entry = ?d', $next);
             $next = $row['next_page'];
             $this->pageText[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
         }
     }
     // subItems
     $this->subject->initSubItems();
     if (!empty($this->subject->subItems[$this->typeId])) {
         uaSort($this->subject->subItems[$this->typeId], function ($a, $b) {
             return strcmp($a['name'], $b['name']);
         });
         $this->subItems = array('data' => array_values($this->subject->subItems[$this->typeId]), 'quality' => $this->subject->getField('quality'));
         // merge identical stats and names for normal users (e.g. spellPower of a specific school became generel spellPower with 3.0)
         if (!User::isInGroup(U_GROUP_EMPLOYEE)) {
             for ($i = 1; $i < count($this->subItems['data']); $i++) {
                 $prev =& $this->subItems['data'][$i - 1];
                 $cur =& $this->subItems['data'][$i];
                 if ($prev['jsonequip'] == $cur['jsonequip'] && $prev['name'] == $cur['name']) {
                     $prev['chance'] += $cur['chance'];
                     array_splice($this->subItems['data'], $i, 1);
                     $i = 1;
                 }
             }
         }
     }
     // factionchange-equivalent
     if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_items WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId)) {
         $altItem = new ItemList(array(['id', abs($pendant)]));
         if (!$altItem->error) {
             $this->transfer = sprintf(Lang::item('_transfer'), $altItem->id, $altItem->getField('quality'), $altItem->getField('iconString'), $altItem->getField('name', true), $pendant > 0 ? 'alliance' : 'horde', $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2));
         }
     }
     /**************/
     /* Extra Tabs */
     /**************/
     // tabs: this item is contained in..
     $lootTabs = new Loot();
     $createdBy = [];
     if ($lootTabs->getByItem($this->typeId)) {
         $this->extendGlobalData($lootTabs->jsGlobals);
         foreach ($lootTabs->iterate() as $idx => $tab) {
             if (!$tab[1]) {
                 continue;
             }
             if ($idx == 16) {
                 $createdBy = array_column($tab[1], 'id');
             }
             $this->lvTabs[] = array('file' => $tab[0], 'data' => $tab[1], 'params' => ['name' => $tab[2], 'id' => $tab[3], 'extraCols' => $tab[4] ? '$[' . implode(', ', array_unique($tab[4])) . ']' : null, 'hiddenCols' => $tab[5] ? '$ ' . Util::toJSON(array_unique($tab[5])) : null, 'visibleCols' => $tab[6] ? '$' . Util::toJSON(array_unique($tab[6])) : null]);
         }
     }
     // tabs: this item contains..
     $sourceFor = array([LOOT_ITEM, $this->subject->id, '$LANG.tab_contains', 'contains', ['Listview.extraCols.percent'], [], []], [LOOT_PROSPECTING, $this->subject->id, '$LANG.tab_prospecting', 'prospecting', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], [LOOT_MILLING, $this->subject->id, '$LANG.tab_milling', 'milling', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], [LOOT_DISENCHANT, $this->subject->getField('disenchantId'), '$LANG.tab_disenchanting', 'disenchanting', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []]);
     $reqQuest = [];
     foreach ($sourceFor as $sf) {
         $lootTab = new Loot();
         if ($lootTab->getByContainer($sf[0], $sf[1])) {
             $this->extendGlobalData($lootTab->jsGlobals);
             $sf[4] = array_merge($sf[4], $lootTab->extraCols);
             foreach ($lootTab->iterate() as $lv) {
                 if (!$lv['quest']) {
                     continue;
                 }
                 $sf[4] = array_merge($sf[4], ['Listview.extraCols.condition']);
                 $reqQuest[$lv['id']] = 0;
                 $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
             }
             $this->lvTabs[] = array('file' => 'item', 'data' => $lootTab->getResult(), 'params' => ['name' => $sf[2], 'id' => $sf[3], 'extraCols' => $sf[4] ? "\$[" . implode(', ', array_unique($sf[4])) . "]" : null, 'hiddenCols' => $sf[5] ? "\$" . Util::toJSON($sf[5]) : null, 'visibleCols' => $sf[6] ? '$' . Util::toJSON($sf[6]) : null]);
         }
     }
     if ($reqIds = array_keys($reqQuest)) {
         $conditions = array('OR', ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds], ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds], ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds], ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds]);
         $reqQuests = new QuestList($conditions);
         $reqQuests->getJSGlobals(GLOBALINFO_SELF);
         foreach ($reqQuests->iterate() as $qId => $__) {
             if (empty($reqQuests->requires[$qId][TYPE_ITEM])) {
                 continue;
             }
             foreach ($reqIds as $rId) {
                 if (in_array($rId, $reqQuests->requires[$qId][TYPE_ITEM])) {
                     $reqQuest[$rId] = $reqQuests->id;
                 }
             }
         }
     }
     // tab: container can contain
     if ($this->subject->getField('slots') > 0) {
         $contains = new ItemList(array(['bagFamily', $_bagFamily, '&'], ['slots', 1, '<'], CFG_SQL_LIMIT_NONE));
         if (!$contains->error) {
             $this->extendGlobalData($contains->getJSGlobals(GLOBALINFO_SELF));
             $hCols = ['side'];
             if (!$contains->hasSetFields(['slot'])) {
                 $hCols[] = 'slot';
             }
             $this->lvTabs[] = array('file' => 'item', 'data' => $contains->getListviewData(), 'params' => ['name' => '$LANG.tab_cancontain', 'id' => 'can-contain', 'hiddenCols' => '$' . Util::toJSON($hCols)]);
         }
     } else {
         if ($_bagFamily != 0x100) {
             $contains = new ItemList(array(['bagFamily', $_bagFamily, '&'], ['slots', 0, '>'], CFG_SQL_LIMIT_NONE));
             if (!$contains->error) {
                 $this->extendGlobalData($contains->getJSGlobals(GLOBALINFO_SELF));
                 $this->lvTabs[] = array('file' => 'item', 'data' => $contains->getListviewData(), 'params' => ['name' => '$LANG.tab_canbeplacedin', 'id' => 'can-be-placed-in', 'hiddenCols' => "\$['side']"]);
             }
         }
     }
     // tab: criteria of
     $conditions = array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM]], ['ac.value1', $this->typeId]);
     $criteriaOf = new AchievementList($conditions);
     if (!$criteriaOf->error) {
         $this->extendGlobalData($criteriaOf->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
         $hCols = [];
         if (!$criteriaOf->hasSetFields(['reward_loc0'])) {
             $hCols = ['rewards'];
         }
         $this->lvTabs[] = array('file' => 'achievement', 'data' => $criteriaOf->getListviewData(), 'params' => ['name' => '$LANG.tab_criteriaof', 'id' => 'criteria-of', 'visibleCols' => "\$['category']", 'hiddenCols' => '$' . Util::toJSON($hCols)]);
     }
     // tab: reagent for
     $conditions = array('OR', ['reagent1', $this->typeId], ['reagent2', $this->typeId], ['reagent3', $this->typeId], ['reagent4', $this->typeId], ['reagent5', $this->typeId], ['reagent6', $this->typeId], ['reagent7', $this->typeId], ['reagent8', $this->typeId]);
     $reagent = new SpellList($conditions);
     if (!$reagent->error) {
         $this->extendGlobalData($reagent->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
         $this->lvTabs[] = array('file' => 'spell', 'data' => $reagent->getListviewData(), 'params' => ['name' => '$LANG.tab_reagentfor', 'id' => 'reagent-for', 'visibleCols' => "\$['reagents']"]);
     }
     // tab: unlocks (object or item)
     $lockIds = DB::Aowow()->selectCol('SELECT id FROM ?_lock WHERE        (type1 = 1 AND properties1 = ?d) OR
         (type2 = 1 AND properties2 = ?d) OR (type3 = 1 AND properties3 = ?d) OR
         (type4 = 1 AND properties4 = ?d) OR (type5 = 1 AND properties5 = ?d)', $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId);
     if ($lockIds) {
         // objects
         $lockedObj = new GameObjectList(array(['lockId', $lockIds]));
         if (!$lockedObj->error) {
             $this->lvTabs[] = array('file' => 'object', 'data' => $lockedObj->getListviewData(), 'params' => ['name' => '$LANG.tab_unlocks', 'id' => 'unlocks-object']);
         }
         // items (generally unused. It's the spell on the item, that unlocks stuff)
         $lockedItm = new ItemList(array(['lockId', $lockIds]));
         if (!$lockedItm->error) {
             $this->extendGlobalData($lockedItm->getJSGlobals(GLOBALINFO_SELF));
             $this->lvTabs[] = array('file' => 'item', 'data' => $lockedItm->getListviewData(), 'params' => ['name' => '$LANG.tab_unlocks', 'id' => 'unlocks-item']);
         }
     }
     // tab: see also
     $conditions = array(['id', $this->typeId, '!'], ['OR', ['name_loc' . User::$localeId, $this->subject->getField('name', true)], ['AND', ['class', $_class], ['subClass', $_subClass], ['slot', $_slot], ['itemLevel', $this->subject->getField('itemLevel') - 15, '>'], ['itemLevel', $this->subject->getField('itemLevel') + 15, '<'], ['quality', $this->subject->getField('quality')], ['requiredClass', $this->subject->getField('requiredClass')]]]);
     $saItems = new ItemList($conditions);
     if (!$saItems->error) {
         $this->extendGlobalData($saItems->getJSGlobals(GLOBALINFO_SELF));
         $this->lvTabs[] = array('file' => 'item', 'data' => $saItems->getListviewData(), 'params' => ['name' => '$LANG.tab_seealso', 'id' => 'see-also']);
     }
     // tab: starts (quest)
     if ($qId = $this->subject->getField('startQuest')) {
         $starts = new QuestList(array(['id', $qId]));
         if (!$starts->error) {
             $this->extendGlobalData($starts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
             $this->lvTabs[] = array('file' => 'quest', 'data' => $starts->getListviewData(), 'params' => ['name' => '$LANG.tab_starts', 'id' => 'starts-quest']);
         }
     }
     // tab: objective of (quest)
     $conditions = array('OR', ['reqItemId1', $this->typeId], ['reqItemId2', $this->typeId], ['reqItemId3', $this->typeId], ['reqItemId4', $this->typeId], ['reqItemId5', $this->typeId], ['reqItemId6', $this->typeId]);
     $objective = new QuestList($conditions);
     if (!$objective->error) {
         $this->extendGlobalData($objective->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
         $this->lvTabs[] = array('file' => 'quest', 'data' => $objective->getListviewData(), 'params' => ['name' => '$LANG.tab_objectiveof', 'id' => 'objective-of-quest']);
     }
     // tab: provided for (quest)
     $conditions = array('OR', ['sourceItemId', $this->typeId], ['reqSourceItemId1', $this->typeId], ['reqSourceItemId2', $this->typeId], ['reqSourceItemId3', $this->typeId], ['reqSourceItemId4', $this->typeId]);
     $provided = new QuestList($conditions);
     if (!$provided->error) {
         $this->extendGlobalData($provided->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
         $this->lvTabs[] = array('file' => 'quest', 'data' => $provided->getListviewData(), 'params' => ['name' => '$LANG.tab_providedfor', 'id' => 'provided-for-quest']);
     }
     // tab: same model as
     // todo (low): should also work for creatures summoned by item
     if (($model = $this->subject->getField('model')) && $_slot) {
         $sameModel = new ItemList(array(['model', $model], ['id', $this->typeId, '!'], ['slot', $_slot]));
         if (!$sameModel->error) {
             $this->extendGlobalData($sameModel->getJSGlobals(GLOBALINFO_SELF));
             $this->lvTabs[] = array('file' => 'genericmodel', 'data' => $sameModel->getListviewData(ITEMINFO_MODEL), 'params' => ['name' => '$LANG.tab_samemodelas', 'id' => 'same-model-as', 'genericlinktype' => 'item']);
         }
     }
     // tab: sold by
     if (!empty($this->subject->getExtendedCost()[$this->subject->id])) {
         $vendors = $this->subject->getExtendedCost()[$this->subject->id];
         $soldBy = new CreatureList(array(['id', array_keys($vendors)]));
         if (!$soldBy->error) {
             $sbData = $soldBy->getListviewData();
             $this->extendGlobalData($soldBy->getJSGlobals(GLOBALINFO_SELF));
             $extraCols = ['Listview.extraCols.stock', "Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost'];
             $holidays = [];
             foreach ($sbData as $k => &$row) {
                 $currency = [];
                 $tokens = [];
                 foreach ($vendors[$k] as $id => $qty) {
                     if (is_string($id)) {
                         continue;
                     }
                     if ($id > 0) {
                         $tokens[] = [$id, $qty];
                     } else {
                         if ($id < 0) {
                             $currency[] = [-$id, $qty];
                         }
                     }
                 }
                 if ($currency) {
                     $this->extendGlobalIds(TYPE_CURRENCY, array_column($currency, 0));
                 }
                 if ($tokens) {
                     $this->extendGlobalIds(TYPE_ITEM, array_column($tokens, 0));
                 }
                 $row['stock'] = $vendors[$k]['stock'];
                 $row['cost'] = [empty($vendors[$k][0]) ? 0 : $vendors[$k][0]];
                 if ($e = $vendors[$k]['event']) {
                     if (count($extraCols) == 3) {
                         $extraCols[] = 'Listview.extraCols.condition';
                     }
                     $this->extendGlobalIds(TYPE_WORLDEVENT, $e);
                     $row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $e]];
                 }
                 if ($currency || $tokens) {
                     // fill idx:3 if required
                     $row['cost'][] = $currency;
                 }
                 if ($tokens) {
                     $row['cost'][] = $tokens;
                 }
                 if ($x = $this->subject->getField('buyPrice')) {
                     $row['buyprice'] = $x;
                 }
                 if ($x = $this->subject->getField('sellPrice')) {
                     $row['sellprice'] = $x;
                 }
                 if ($x = $this->subject->getField('buyCount')) {
                     $row['stack'] = $x;
                 }
             }
             $this->lvTabs[] = array('file' => 'creature', 'data' => $sbData, 'params' => ['name' => '$LANG.tab_soldby', 'id' => 'sold-by-npc', 'extraCols' => '$[' . implode(', ', $extraCols) . ']', 'hiddenCols' => "\$['level', 'type']"]);
         }
     }
     // tab: currency for
     // some minor trickery: get arenaPoints(43307) and honorPoints(43308) directly
     if ($this->typeId == 43307) {
         $n = '?items&filter=cr=145;crs=1;crv=0';
         $w = 'reqArenaPoints > 0';
     } else {
         if ($this->typeId == 43308) {
             $n = '?items&filter=cr=144;crs=1;crv=0';
             $w = 'reqHonorPoints > 0';
         } else {
             $n = in_array($this->typeId, [42, 61, 81, 241, 121, 122, 123, 125, 126, 161, 201, 101, 102, 221, 301, 341]) ? '?items&filter=cr=158;crs=' . $this->typeId . ';crv=0' : null;
             $w = 'reqItemId1 = ' . $this->typeId . ' OR reqItemId2 = ' . $this->typeId . ' OR reqItemId3 = ' . $this->typeId . ' OR reqItemId4 = ' . $this->typeId . ' OR reqItemId5 = ' . $this->typeId;
         }
     }
     $xCosts = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE ' . $w);
     $boughtBy = $xCosts ? DB::World()->selectCol('SELECT item FROM npc_vendor WHERE extendedCost IN (?a) UNION SELECT item FROM game_event_npc_vendor WHERE extendedCost IN (?a)', $xCosts, $xCosts) : null;
     if ($boughtBy) {
         $boughtBy = new ItemList(array(['id', $boughtBy]));
         if (!$boughtBy->error) {
             if ($boughtBy->getMatches() <= CFG_SQL_LIMIT_DEFAULT) {
                 $n = null;
             }
             $iCur = new CurrencyList(array(['itemId', $this->typeId]));
             $filter = $iCur->error ? [TYPE_ITEM => $this->typeId] : [TYPE_CURRENCY => $iCur->id];
             $this->lvTabs[] = array('file' => 'item', 'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, $filter), 'params' => ['name' => '$LANG.tab_currencyfor', 'id' => 'currency-for', 'extraCols' => "\$[Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack'), Listview.extraCols.cost]", 'note' => $n ? sprintf(Util::$filterResultString, $n) : null]);
             $this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_ANY));
         }
     }
     // tab: teaches
     $ids = $indirect = [];
     for ($i = 1; $i < 6; $i++) {
         if ($this->subject->getField('spellTrigger' . $i) == 6) {
             $ids[] = $this->subject->getField('spellId' . $i);
         } else {
             if ($this->subject->getField('spellTrigger' . $i) == 0 && $this->subject->getField('spellId' . $i) > 0) {
                 $indirect[] = $this->subject->getField('spellId' . $i);
             }
         }
     }
     // taught indirectly
     if ($indirect) {
         $indirectSpells = new SpellList(array(['id', $indirect]));
         foreach ($indirectSpells->iterate() as $__) {
             if ($_ = $indirectSpells->canTeachSpell()) {
                 foreach ($_ as $idx) {
                     $ids[] = $indirectSpells->getField('effect' . $idx . 'TriggerSpell');
                 }
             }
         }
         $ids = array_merge($ids, Util::getTaughtSpells($indirect));
     }
     if ($ids) {
         $taughtSpells = new SpellList(array(['id', $ids]));
         if (!$taughtSpells->error) {
             $this->extendGlobalData($taughtSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
             $visCols = ['level', 'schools'];
             if ($taughtSpells->hasSetFields(['reagent1'])) {
                 $visCols[] = 'reagents';
             }
             $this->lvTabs[] = array('file' => 'spell', 'data' => $taughtSpells->getListviewData(), 'params' => ['name' => '$LANG.tab_teaches', 'id' => 'teaches', 'visibleCols' => '$' . Util::toJSON($visCols)]);
         }
     }
     // tab: Shared cooldown
     $cdCats = [];
     for ($i = 1; $i < 6; $i++) {
         if ($this->subject->getField('spellId' . $i) > 0 && $this->subject->getField('spellCategory' . $i) > 0) {
             $cdCats[] = $this->subject->getField('spellCategory' . $i);
         }
     }
     if ($cdCats) {
         $conditions = array('OR', ['spellCategory1', $cdCats], ['spellCategory2', $cdCats], ['spellCategory3', $cdCats], ['spellCategory4', $cdCats], ['spellCategory5', $cdCats]);
         $cdItems = new ItemList($conditions);
         if (!$cdItems->error) {
             $this->lvTabs[] = array('file' => 'item', 'data' => $cdItems->getListviewData(), 'params' => ['name' => '$LANG.tab_sharedcooldown', 'id' => 'shared-cooldown']);
             $this->extendGlobalData($cdItems->getJSGlobals(GLOBALINFO_SELF));
         }
     }
     // // todo - tab: taught by
     // use var $createdBy to find source of this spell
     // id: 'taught-by-X',
     // name: LANG.tab_taughtby
 }