コード例 #1
0
ファイル: item.class.php プロジェクト: saqar/aowow
 public function renderTooltip($interactive = false, $subOf = 0, $enhance = [])
 {
     if ($this->error) {
         return;
     }
     $_name = $this->getField('name', true);
     $_reqLvl = $this->curTpl['requiredLevel'];
     $_quality = $this->curTpl['quality'];
     $_flags = $this->curTpl['flags'];
     $_class = $this->curTpl['class'];
     $_subClass = $this->curTpl['subClass'];
     $_slot = $this->curTpl['slot'];
     $causesScaling = false;
     if (!empty($enhance['r'])) {
         if ($this->getRandEnchantForItem($enhance['r'])) {
             $_name .= ' ' . Util::localizedString($this->enhanceR, 'name');
             $randEnchant = '';
             for ($i = 1; $i < 6; $i++) {
                 if ($this->enhanceR['enchantId' . $i] <= 0) {
                     continue;
                 }
                 $enchant = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?d', $this->enhanceR['enchantId' . $i]);
                 if ($this->enhanceR['allocationPct' . $i] > 0) {
                     $amount = intVal($this->enhanceR['allocationPct' . $i] * $this->generateEnchSuffixFactor());
                     $randEnchant .= '<span>' . str_replace('$i', $amount, Util::localizedString($enchant, 'name')) . '</span><br />';
                 } else {
                     $randEnchant .= '<span>' . Util::localizedString($enchant, 'name') . '</span><br />';
                 }
             }
         } else {
             unset($enhance['r']);
         }
     }
     if (isset($enhance['s']) && !in_array($_slot, [INVTYPE_WRISTS, INVTYPE_WAIST, INVTYPE_HANDS])) {
         unset($enhance['s']);
     }
     // IMPORTAT: DO NOT REMOVE THE HTML-COMMENTS! THEY ARE REQUIRED TO UPDATE THE TOOLTIP CLIENTSIDE
     $x = '';
     // upper table: stats
     if (!$subOf) {
         $x .= '<table><tr><td>';
     }
     // name; quality
     if ($subOf) {
         $x .= '<span class="q' . $_quality . '"><a href="?item=' . $this->id . '">' . $_name . '</a></span>';
     } else {
         $x .= '<b class="q' . $_quality . '">' . $_name . '</b>';
     }
     // heroic tag
     if ($_flags & ITEM_FLAG_HEROIC && $_quality == ITEM_QUALITY_EPIC) {
         $x .= '<br /><span class="q2">' . Lang::item('heroic') . '</span>';
     }
     // requires map (todo: reparse ?_zones for non-conflicting data; generate Link to zone)
     if ($_ = $this->curTpl['map']) {
         $map = DB::Aowow()->selectRow('SELECT * FROM ?_zones WHERE mapId = ?d LIMIT 1', $_);
         $x .= '<br /><a href="?zone=' . $_ . '" class="q1">' . Util::localizedString($map, 'name') . '</a>';
     }
     // requires area
     if ($this->curTpl['area']) {
         $area = DB::Aowow()->selectRow('SELECT * FROM ?_zones WHERE Id=?d LIMIT 1', $this->curTpl['area']);
         $x .= '<br />' . Util::localizedString($area, 'name');
     }
     // conjured
     if ($_flags & ITEM_FLAG_CONJURED) {
         $x .= '<br />' . Lang::item('conjured');
     }
     // bonding
     if ($_flags & ITEM_FLAG_ACCOUNTBOUND) {
         $x .= '<br /><!--bo-->' . Lang::item('bonding', 0);
     } else {
         if ($this->curTpl['bonding']) {
             $x .= '<br /><!--bo-->' . Lang::item('bonding', $this->curTpl['bonding']);
         }
     }
     // unique || unique-equipped || unique-limited
     if ($this->curTpl['maxCount'] > 0) {
         $x .= '<br />' . Lang::item('unique');
         if ($this->curTpl['maxCount'] > 1) {
             $x .= ' (' . $this->curTpl['maxCount'] . ')';
         }
     } else {
         if ($_flags & ITEM_FLAG_UNIQUEEQUIPPED) {
             $x .= '<br />' . Lang::item('uniqueEquipped');
         } else {
             if ($this->curTpl['itemLimitCategory']) {
                 $limit = DB::Aowow()->selectRow("SELECT * FROM ?_itemlimitcategory WHERE id = ?", $this->curTpl['itemLimitCategory']);
                 $x .= '<br />' . ($limit['isGem'] ? Lang::item('uniqueEquipped') : Lang::item('unique')) . Lang::main('colon') . Util::localizedString($limit, 'name') . ' (' . $limit['count'] . ')';
             }
         }
     }
     // max duration
     if ($dur = $this->curTpl['duration']) {
         $x .= "<br />" . Lang::game('duration') . Lang::main('colon') . Util::formatTime(abs($dur) * 1000) . ($this->curTpl['flagsCustom'] & 0x1 ? ' (' . Lang::item('realTime') . ')' : null);
     }
     // required holiday
     if ($eId = $this->curTpl['eventId']) {
         if ($hName = DB::Aowow()->selectRow('SELECT h.* FROM ?_holidays h JOIN ?_events e ON e.holidayId = h.id WHERE e.id = ?d', $eId)) {
             $x .= '<br />' . sprintf(Lang::game('requires'), '<a href="' . $eId . '" class="q1">' . Util::localizedString($hName, 'name') . '</a>');
         }
     }
     // item begins a quest
     if ($this->curTpl['startQuest']) {
         $x .= '<br /><a class="q1" href="?quest=' . $this->curTpl['startQuest'] . '">' . Lang::item('startQuest') . '</a>';
     }
     // containerType (slotCount)
     if ($this->curTpl['slots'] > 0) {
         $fam = $this->curTpl['bagFamily'] ? log($this->curTpl['bagFamily'], 2) + 1 : 0;
         // word order differs <_<
         if (in_array(User::$localeId, [LOCALE_FR, LOCALE_ES, LOCALE_RU])) {
             $x .= '<br />' . sprintf(Lang::item('bagSlotString'), Lang::item('bagFamily', $fam), $this->curTpl['slots']);
         } else {
             $x .= '<br />' . sprintf(Lang::item('bagSlotString'), $this->curTpl['slots'], Lang::item('bagFamily', $fam));
         }
     }
     if (in_array($_class, [ITEM_CLASS_ARMOR, ITEM_CLASS_WEAPON, ITEM_CLASS_AMMUNITION])) {
         $x .= '<table width="100%"><tr>';
         // Class
         if ($_slot) {
             $x .= '<td>' . Lang::item('inventoryType', $_slot) . '</td>';
         }
         // Subclass
         if ($_class == ITEM_CLASS_ARMOR && $_subClass > 0) {
             $x .= '<th><!--asc' . $_subClass . '-->' . Lang::item('armorSubClass', $_subClass) . '</th>';
         } else {
             if ($_class == ITEM_CLASS_WEAPON) {
                 $x .= '<th>' . Lang::item('weaponSubClass', $_subClass) . '</th>';
             } else {
                 if ($_class == ITEM_CLASS_AMMUNITION) {
                     $x .= '<th>' . Lang::item('projectileSubClass', $_subClass) . '</th>';
                 }
             }
         }
         $x .= '</tr></table>';
     } else {
         if ($_slot && $_class != ITEM_CLASS_CONTAINER) {
             // yes, slot can occur on random items and is then also displayed <_< .. excluding Bags >_>
             $x .= '<br />' . Lang::item('inventoryType', $_slot) . '<br />';
         } else {
             $x .= '<br />';
         }
     }
     // Weapon/Ammunition Stats                          (not limited to weapons (see item:1700))
     $speed = $this->curTpl['delay'] / 1000;
     $dmgmin1 = $this->curTpl['dmgMin1'] + $this->curTpl['dmgMin2'];
     $dmgmax1 = $this->curTpl['dmgMax1'] + $this->curTpl['dmgMax2'];
     $dps = $speed ? ($dmgmin1 + $dmgmax1) / (2 * $speed) : 0;
     if ($_class == ITEM_CLASS_AMMUNITION && $dmgmin1 && $dmgmax1) {
         $x .= Lang::item('addsDps') . ' ' . number_format(($dmgmin1 + $dmgmax1) / 2, 1) . ' ' . Lang::item('dps2') . '<br />';
     } else {
         if ($dps) {
             if ($_class == ITEM_CLASS_WEAPON) {
                 $x .= '<table width="100%"><tr>';
                 $x .= '<td><!--dmg-->' . sprintf($this->curTpl['dmgType1'] ? Lang::item('damageMagic') : Lang::item('damagePhys'), $this->curTpl['dmgMin1'] . ' - ' . $this->curTpl['dmgMax1'], Lang::game('sc', $this->curTpl['dmgType1'])) . '</td>';
                 $x .= '<th>' . Lang::item('speed') . ' <!--spd-->' . number_format($speed, 2) . '</th>';
                 // do not use localized format here!
                 $x .= '</tr></table>';
             } else {
                 $x .= '<!--dmg-->' . sprintf($this->curTpl['dmgType1'] ? Lang::item('damageMagic') : Lang::item('damagePhys'), $this->curTpl['dmgMin1'] . ' - ' . $this->curTpl['dmgMax1'], Lang::game('sc', $this->curTpl['dmgType1'])) . '<br />';
             }
             // secondary damage is set
             if ($this->curTpl['dmgMin2']) {
                 $x .= '+' . sprintf($this->curTpl['dmgType2'] ? Lang::item('damageMagic') : Lang::item('damagePhys'), $this->curTpl['dmgMin2'] . ' - ' . $this->curTpl['dmgMax2'], Lang::game('sc', $this->curTpl['dmgType2'])) . '<br />';
             }
             if ($_class == ITEM_CLASS_WEAPON) {
                 $x .= '<!--dps-->(' . number_format($dps, 1) . ' ' . Lang::item('dps') . ')<br />';
             }
             // do not use localized format here!
             // display FeralAttackPower if set
             if ($fap = $this->getFeralAP()) {
                 $x .= '<span class="c11"><!--fap-->(' . $fap . ' ' . Lang::item('fap') . ')</span><br />';
             }
         }
     }
     // Armor
     if ($_class == ITEM_CLASS_ARMOR && $this->curTpl['armorDamageModifier'] > 0) {
         $spanI = 'class="q2"';
         if ($interactive) {
             $spanI = 'class="q2 tip" onmouseover="$WH.Tooltip.showAtCursor(event, $WH.sprintf(LANG.tooltip_armorbonus, ' . $this->curTpl['armorDamageModifier'] . '), 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"';
         }
         $x .= '<span ' . $spanI . '><!--addamr' . $this->curTpl['armorDamageModifier'] . '--><span>' . sprintf(Lang::item('armor'), intVal($this->curTpl['armor'] + $this->curTpl['armorDamageModifier'])) . '</span></span><br />';
     } else {
         if ($this->curTpl['armor'] + $this->curTpl['armorDamageModifier'] > 0) {
             $x .= '<span><!--amr-->' . sprintf(Lang::item('armor'), intVal($this->curTpl['armor'] + $this->curTpl['armorDamageModifier'])) . '</span><br />';
         }
     }
     // Block
     if ($this->curTpl['block']) {
         $x .= '<span>' . sprintf(Lang::item('block'), $this->curTpl['block']) . '</span><br />';
     }
     // Item is a gem (don't mix with sockets)
     if ($geId = $this->curTpl['gemEnchantmentId']) {
         $gemEnch = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE id = ?d', $geId);
         $x .= '<span class="q1"><a href="?enchantment=' . $geId . '">' . Util::localizedString($gemEnch, 'name') . '</a></span><br />';
         // activation conditions for meta gems
         if (!empty($gemEnch['conditionId'])) {
             if ($gemCnd = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantmentcondition WHERE id = ?d', $gemEnch['conditionId'])) {
                 for ($i = 1; $i < 6; $i++) {
                     if (!$gemCnd['color' . $i]) {
                         continue;
                     }
                     switch ($gemCnd['comparator' . $i]) {
                         case 2:
                             // requires less <color> than (<value> || <comparecolor>) gems
                         // requires less <color> than (<value> || <comparecolor>) gems
                         case 5:
                             // requires at least <color> than (<value> || <comparecolor>) gems
                             $sp = (int) $gemCnd['value' . $i] > 1;
                             $x .= '<span class="q0">' . Lang::achievement('reqNumCrt') . ' ' . sprintf(Lang::item('gemConditions', $gemCnd['comparator' . $i], $sp), $gemCnd['value' . $i], Lang::item('gemColors', $gemCnd['color' . $i] - 1)) . '</span><br />';
                             break;
                         case 3:
                             // requires more <color> than (<value> || <comparecolor>) gems
                             $x .= '<span class="q0">' . Lang::achievement('reqNumCrt') . ' ' . sprintf(Lang::item('gemConditions', 3), Lang::item('gemColors', $gemCnd['color' . $i] - 1), Lang::item('gemColors', $gemCnd['cmpColor' . $i] - 1)) . '</span><br />';
                             break;
                     }
                 }
             }
         }
     }
     // Random Enchantment - if random enchantment is set, prepend stats from it
     if ($this->curTpl['randomEnchant'] && !isset($enhance['r'])) {
         $x .= '<span class="q2">' . Lang::item('randEnchant') . '</span><br />';
     } else {
         if (isset($enhance['r'])) {
             $x .= $randEnchant;
         }
     }
     // itemMods (display stats and save ratings for later use)
     for ($j = 1; $j <= 10; $j++) {
         $type = $this->curTpl['statType' . $j];
         $qty = $this->curTpl['statValue' . $j];
         if (!$qty || $type <= 0) {
             continue;
         }
         // base stat
         if ($type >= ITEM_MOD_AGILITY && $type <= ITEM_MOD_STAMINA) {
             $x .= '<span><!--stat' . $type . '-->' . ($qty > 0 ? '+' : '-') . abs($qty) . ' ' . Lang::item('statType', $type) . '</span><br />';
         } else {
             // rating with % for reqLevel
             $green[] = $this->parseRating($type, $qty, $interactive, $causesScaling);
         }
     }
     // magic resistances
     foreach (Util::$resistanceFields as $j => $rowName) {
         if ($rowName && $this->curTpl[$rowName] != 0) {
             $x .= '+' . $this->curTpl[$rowName] . ' ' . Lang::game('resistances', $j) . '<br />';
         }
     }
     // Enchantment
     if (isset($enhance['e'])) {
         if ($enchText = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?', $enhance['e'])) {
             $x .= '<span class="q2"><!--e-->' . Util::localizedString($enchText, 'name') . '</span><br />';
         } else {
             unset($enhance['e']);
             $x .= '<!--e-->';
         }
     } else {
         // enchantment placeholder
         $x .= '<!--e-->';
     }
     // Sockets w/ Gems
     if (!empty($enhance['g'])) {
         $gems = DB::Aowow()->select('
             SELECT it.id AS ARRAY_KEY, ic.iconString, ae.*, it.gemColorMask AS colorMask
             FROM   ?_items it
             JOIN   ?_itemenchantment ae ON ae.id = it.gemEnchantmentId
             JOIN   ?_icons ic ON ic.id = -it.displayId
             WHERE  it.id IN (?a)', $enhance['g']);
         foreach ($enhance['g'] as $k => $v) {
             if ($v && !in_array($v, array_keys($gems))) {
                 // 0 is valid
                 unset($enhance['g'][$k]);
             }
         }
     } else {
         $enhance['g'] = [];
     }
     // zero fill empty sockets
     $sockCount = isset($enhance['s']) ? 1 : 0;
     if (!empty($this->json[$this->id]['nsockets'])) {
         $sockCount += $this->json[$this->id]['nsockets'];
     }
     while ($sockCount > count($enhance['g'])) {
         $enhance['g'][] = 0;
     }
     $enhance['g'] = array_reverse($enhance['g']);
     $hasMatch = 1;
     // fill native sockets
     for ($j = 1; $j <= 3; $j++) {
         if (!$this->curTpl['socketColor' . $j]) {
             continue;
         }
         for ($i = 0; $i < 4; $i++) {
             if ($this->curTpl['socketColor' . $j] & 1 << $i) {
                 $colorId = $i;
             }
         }
         $pop = array_pop($enhance['g']);
         $col = $pop ? 1 : 0;
         $hasMatch &= $pop ? $gems[$pop]['colorMask'] & 1 << $colorId ? 1 : 0 : 0;
         $icon = $pop ? sprintf(Util::$bgImagePath['tiny'], STATIC_URL, strtolower($gems[$pop]['iconString'])) : null;
         $text = $pop ? Util::localizedString($gems[$pop], 'name') : Lang::item('socket', $colorId);
         if ($interactive) {
             $x .= '<a href="?items=3&amp;filter=cr=81;crs=' . ($colorId + 1) . ';crv=0" class="socket-' . Util::$sockets[$colorId] . ' q' . $col . '" ' . $icon . '>' . $text . '</a><br />';
         } else {
             $x .= '<span class="socket-' . Util::$sockets[$colorId] . ' q' . $col . '" ' . $icon . '>' . $text . '</span><br />';
         }
     }
     // fill extra socket
     if (isset($enhance['s'])) {
         $pop = array_pop($enhance['g']);
         $col = $pop ? 1 : 0;
         $icon = $pop ? sprintf(Util::$bgImagePath['tiny'], STATIC_URL, strtolower($gems[$pop]['iconString'])) : null;
         $text = $pop ? Util::localizedString($gems[$pop], 'name') : Lang::item('socket', -1);
         if ($interactive) {
             $x .= '<a href="?items=3&amp;filter=cr=81;crs=5;crv=0" class="socket-prismatic q' . $col . '" ' . $icon . '>' . $text . '</a><br />';
         } else {
             $x .= '<span class="socket-prismatic q' . $col . '" ' . $icon . '>' . $text . '</span><br />';
         }
     } else {
         // prismatic socket placeholder
         $x .= '<!--ps-->';
     }
     if ($_ = $this->curTpl['socketBonus']) {
         $sbonus = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?d', $_);
         $x .= '<span class="q' . ($hasMatch ? '2' : '0') . '">' . Lang::item('socketBonus') . Lang::main('colon') . '<a href="?enchantment=' . $_ . '">' . Util::localizedString($sbonus, 'name') . '</a></span><br />';
     }
     // durability
     if ($dur = $this->curTpl['durability']) {
         $x .= Lang::item('durability') . ' ' . $dur . ' / ' . $dur . '<br />';
     }
     // required classes
     if ($classes = Lang::getClassString($this->curTpl['requiredClass'], $jsg, $__)) {
         foreach ($jsg as $js) {
             if (empty($this->jsGlobals[TYPE_CLASS][$js])) {
                 $this->jsGlobals[TYPE_CLASS][$js] = $js;
             }
         }
         $x .= Lang::game('classes') . Lang::main('colon') . $classes . '<br />';
     }
     // required races
     if ($races = Lang::getRaceString($this->curTpl['requiredRace'], $__, $jsg, $__)) {
         foreach ($jsg as $js) {
             if (empty($this->jsGlobals[TYPE_RACE][$js])) {
                 $this->jsGlobals[TYPE_RACE][$js] = $js;
             }
         }
         if ($races != Lang::game('ra', 0)) {
             // not "both", but display combinations like: troll, dwarf
             $x .= Lang::game('races') . Lang::main('colon') . $races . '<br />';
         }
     }
     // required honorRank (not used anymore)
     if ($rhr = $this->curTpl['requiredHonorRank']) {
         $x .= sprintf(Lang::game('requires'), Lang::game('pvpRank', $rhr)) . '<br />';
     }
     // required CityRank..?
     // what the f..
     // required level
     if ($_flags & ITEM_FLAG_ACCOUNTBOUND && $_quality == ITEM_QUALITY_HEIRLOOM) {
         $x .= sprintf(Lang::game('reqLevelHlm'), ' 1' . Lang::game('valueDelim') . MAX_LEVEL . ' (' . ($interactive ? sprintf(Util::$changeLevelString, MAX_LEVEL) : '<!--lvl-->' . MAX_LEVEL) . ')') . '<br />';
     } else {
         if ($_reqLvl > 1) {
             $x .= sprintf(Lang::game('reqLevel'), $_reqLvl) . '<br />';
         }
     }
     // required arena team rating / personal rating / todo (low): sort out what kind of rating
     if (!empty($this->getExtendedCost([], $reqRating)[$this->id]) && $reqRating) {
         $x .= sprintf(Lang::item('reqRating', $reqRating[1]), $reqRating[0]) . '<br />';
     }
     // item level
     if (in_array($_class, [ITEM_CLASS_ARMOR, ITEM_CLASS_WEAPON])) {
         $x .= Lang::item('itemLevel') . ' ' . $this->curTpl['itemLevel'] . '<br />';
     }
     // required skill
     if ($reqSkill = $this->curTpl['requiredSkill']) {
         $_ = '<a class="q1" href="?skill=' . $reqSkill . '">' . SkillList::getName($reqSkill) . '</a>';
         if ($this->curTpl['requiredSkillRank'] > 0) {
             $_ .= ' (' . $this->curTpl['requiredSkillRank'] . ')';
         }
         $x .= sprintf(Lang::game('requires'), $_) . '<br />';
     }
     // required spell
     if ($reqSpell = $this->curTpl['requiredSpell']) {
         $x .= Lang::game('requires2') . ' <a class="q1" href="?spell=' . $reqSpell . '">' . SpellList::getName($reqSpell) . '</a><br />';
     }
     // required reputation w/ faction
     if ($reqFac = $this->curTpl['requiredFaction']) {
         $x .= sprintf(Lang::game('requires'), '<a class="q1" href=?faction="' . $reqFac . '">' . FactionList::getName($reqFac) . '</a> - ' . Lang::game('rep', $this->curTpl['requiredFactionRank'])) . '<br />';
     }
     // locked or openable
     if ($locks = Lang::getLocks($this->curTpl['lockId'], true)) {
         $x .= '<span class="q0">' . Lang::item('locked') . '<br />' . implode('<br />', $locks) . '</span><br />';
     } else {
         if ($this->curTpl['flags'] & ITEM_FLAG_OPENABLE) {
             $x .= '<span class="q2">' . Lang::item('openClick') . '</span><br />';
         }
     }
     // upper table: done
     if (!$subOf) {
         $x .= '</td></tr></table>';
     }
     // spells on item
     if (!$this->canTeachSpell()) {
         $itemSpellsAndTrigger = [];
         for ($j = 1; $j <= 5; $j++) {
             if ($this->curTpl['spellId' . $j] > 0) {
                 $cd = $this->curTpl['spellCooldown' . $j];
                 if ($cd < $this->curTpl['spellCategoryCooldown' . $j]) {
                     $cd = $this->curTpl['spellCategoryCooldown' . $j];
                 }
                 $cd = $cd < 5000 ? null : ' (' . sprintf(Lang::game('cooldown'), Util::formatTime($cd)) . ')';
                 $itemSpellsAndTrigger[$this->curTpl['spellId' . $j]] = [$this->curTpl['spellTrigger' . $j], $cd];
             }
         }
         if ($itemSpellsAndTrigger) {
             $cooldown = '';
             $itemSpells = new SpellList(array(['s.id', array_keys($itemSpellsAndTrigger)]));
             foreach ($itemSpells->iterate() as $__) {
                 if ($parsed = $itemSpells->parseText('description', $_reqLvl > 1 ? $_reqLvl : MAX_LEVEL, false, $causesScaling)[0]) {
                     if ($interactive) {
                         $link = '<a href="?spell=' . $itemSpells->id . '">%s</a>';
                         $parsed = preg_replace_callback('/([^;]*)(&nbsp;<small>.*?<\\/small>)([^&]*)/i', function ($m) use($link) {
                             $m[1] = $m[1] ? sprintf($link, $m[1]) : '';
                             $m[3] = $m[3] ? sprintf($link, $m[3]) : '';
                             return $m[1] . $m[2] . $m[3];
                         }, $parsed, -1, $nMatches);
                         if (!$nMatches) {
                             $parsed = sprintf($link, $parsed);
                         }
                     }
                     $green[] = Lang::item('trigger', $itemSpellsAndTrigger[$itemSpells->id][0]) . $parsed . $itemSpellsAndTrigger[$itemSpells->id][1];
                 }
             }
         }
     }
     // lower table (ratings, spells, ect)
     if (!$subOf) {
         $x .= '<table><tr><td>';
     }
     if (isset($green)) {
         foreach ($green as $j => $bonus) {
             if ($bonus) {
                 $x .= '<span class="q2">' . $bonus . '</span><br />';
             }
         }
     }
     // Item Set
     $pieces = [];
     if ($setId = $this->getField('itemset')) {
         // while Ids can technically be used multiple times the only difference in data are the items used. So it doesn't matter what we get
         $itemset = new ItemsetList(array(['id', $setId]));
         if (!$itemset->error && $itemset->pieceToSet) {
             $pieces = DB::Aowow()->select('
                 SELECT b.id AS ARRAY_KEY, b.name_loc0, b.name_loc2, b.name_loc3, b.name_loc6, b.name_loc8, GROUP_CONCAT(a.id SEPARATOR \':\') AS equiv
                 FROM   ?_items a, ?_items b
                 WHERE  a.slotBak = b.slotBak AND a.itemset = b.itemset AND b.id IN (?a)
                 GROUP BY b.id;', array_keys($itemset->pieceToSet));
             foreach ($pieces as $k => &$p) {
                 $p = '<span><!--si' . $p['equiv'] . '--><a href="?item=' . $k . '">' . Util::localizedString($p, 'name') . '</a></span>';
             }
             $xSet = '<br /><span class="q"><a href="?itemset=' . $itemset->id . '" class="q">' . $itemset->getField('name', true) . '</a> (0/' . count($pieces) . ')</span>';
             if ($skId = $itemset->getField('skillId')) {
                 $xSet .= '<br />' . sprintf(Lang::game('requires'), '<a href="?skills=' . $skId . '" class="q1">' . SkillList::getName($skId) . '</a>');
                 if ($_ = $itemset->getField('skillLevel')) {
                     $xSet .= ' (' . $_ . ')';
                 }
                 $xSet .= '<br />';
             }
             // list pieces
             $xSet .= '<div class="q0 indent">' . implode('<br />', $pieces) . '</div><br />';
             // get bonuses
             $setSpellsAndIdx = [];
             for ($j = 1; $j <= 8; $j++) {
                 if ($_ = $itemset->getField('spell' . $j)) {
                     $setSpellsAndIdx[$_] = $j;
                 }
             }
             $setSpells = [];
             if ($setSpellsAndIdx) {
                 $boni = new SpellList(array(['s.id', array_keys($setSpellsAndIdx)]));
                 foreach ($boni->iterate() as $__) {
                     $setSpells[] = array('tooltip' => $boni->parseText('description', $_reqLvl > 1 ? $_reqLvl : MAX_LEVEL, false, $causesScaling)[0], 'entry' => $itemset->getField('spell' . $setSpellsAndIdx[$boni->id]), 'bonus' => $itemset->getField('bonus' . $setSpellsAndIdx[$boni->id]));
                 }
             }
             // sort and list bonuses
             $xSet .= '<span class="q0">';
             for ($i = 0; $i < count($setSpells); $i++) {
                 for ($j = $i; $j < count($setSpells); $j++) {
                     if ($setSpells[$j]['bonus'] >= $setSpells[$i]['bonus']) {
                         continue;
                     }
                     $tmp = $setSpells[$i];
                     $setSpells[$i] = $setSpells[$j];
                     $setSpells[$j] = $tmp;
                 }
                 $xSet .= '<span>(' . $setSpells[$i]['bonus'] . ') ' . Lang::item('set') . ': <a href="?spell=' . $setSpells[$i]['entry'] . '">' . $setSpells[$i]['tooltip'] . '</a></span>';
                 if ($i < count($setSpells) - 1) {
                     $xSet .= '<br />';
                 }
             }
             $xSet .= '</span>';
         }
     }
     // recipes, vanity pets, mounts
     if ($this->canTeachSpell()) {
         $craftSpell = new SpellList(array(['s.id', intVal($this->curTpl['spellId2'])]));
         if (!$craftSpell->error) {
             $xCraft = '';
             if ($desc = $this->getField('description', true)) {
                 $x .= '<span class="q2">' . Lang::item('trigger', 0) . ' <a href="?spell=' . $this->curTpl['spellId2'] . '">' . $desc . '</a></span><br />';
             }
             // recipe handling (some stray Techniques have subclass == 0), place at bottom of tooltipp
             if ($_class == ITEM_CLASS_RECIPE || $this->curTpl['bagFamily'] == 16) {
                 $craftItem = new ItemList(array(['i.id', (int) $craftSpell->curTpl['effect1CreateItemId']]));
                 if (!$craftItem->error) {
                     if ($itemTT = $craftItem->renderTooltip($interactive, $this->id)) {
                         $xCraft .= '<div><br />' . $itemTT . '</div>';
                     }
                     $reagentItems = [];
                     for ($i = 1; $i <= 8; $i++) {
                         if ($rId = $craftSpell->getField('reagent' . $i)) {
                             $reagentItems[$rId] = $craftSpell->getField('reagentCount' . $i);
                         }
                     }
                     if (isset($xCraft) && $reagentItems) {
                         $reagents = new ItemList(array(['i.id', array_keys($reagentItems)]));
                         $reqReag = [];
                         foreach ($reagents->iterate() as $__) {
                             $reqReag[] = '<a href="?item=' . $reagents->id . '">' . $reagents->getField('name', true) . '</a> (' . $reagentItems[$reagents->id] . ')';
                         }
                         $xCraft .= '<div class="q1 whtt-reagents"><br />' . Lang::game('requires2') . ' ' . implode(', ', $reqReag) . '</div>';
                     }
                 }
             }
         }
     }
     // misc (no idea, how to organize the <br /> better)
     $xMisc = [];
     // itemset: pieces and boni
     if (isset($xSet)) {
         $xMisc[] = $xSet;
     }
     // funny, yellow text at the bottom, omit if we have a recipe
     if ($this->curTpl['description_loc0'] && !$this->canTeachSpell()) {
         $xMisc[] = '<span class="q">"' . $this->getField('description', true) . '"</span>';
     }
     // readable
     if ($this->curTpl['pageTextId']) {
         $xMisc[] = '<span class="q2">' . Lang::item('readClick') . '</span>';
     }
     // charges (i guess checking first spell is enough (single charges not shown))
     if ($this->curTpl['spellCharges1'] > 1 || $this->curTpl['spellCharges1'] < -1) {
         $xMisc[] = '<span class="q1">' . abs($this->curTpl['spellCharges1']) . ' ' . Lang::item('charges') . '</span>';
     }
     // list required reagents
     if (isset($xCraft)) {
         $xMisc[] = $xCraft;
     }
     if ($xMisc) {
         $x .= implode('<br />', $xMisc);
     }
     if ($sp = $this->curTpl['sellPrice']) {
         $x .= '<div class="q1 whtt-sellprice">' . Lang::item('sellPrice') . Lang::main('colon') . Util::formatMoney($sp) . '</div>';
     }
     if (!$subOf) {
         $x .= '</td></tr></table>';
     }
     // tooltip scaling
     if (!isset($xCraft)) {
         $link = [$subOf ? $subOf : $this->id, 1];
         // itemId, scaleMinLevel
         if (isset($this->ssd[$this->id])) {
             array_push($link, $this->ssd[$this->id]['maxLevel'], $this->ssd[$this->id]['maxLevel'], $this->curTpl['scalingStatDistribution'], $this->curTpl['scalingStatValue']);
         } else {
             array_push($link, $causesScaling ? MAX_LEVEL : 1, $_reqLvl > 1 ? $_reqLvl : MAX_LEVEL);
         }
         $x .= '<!--?' . implode(':', $link) . '-->';
     }
     return $x;
 }