public function renderTooltip() { if (!$this->curTpl) { return array(); } $x = '<table><tr><td>'; $x .= '<span class="q' . $this->getField('quality') . '">' . Util::jsEscape($this->getField('name', true)) . '</span><br />'; $nClasses = 0; if ($_ = $this->getField('classMask')) { $cl = Lang::getClassString($_, $__, $nClasses); $x .= Util::ucFirst($nClasses > 1 ? Lang::game('classes') : Lang::game('class')) . Lang::main('colon') . $cl . '<br />'; } if ($_ = $this->getField('contentGroup')) { $x .= Util::jsEscape(Lang::itemset('notes', $_)) . ($this->getField('heroic') ? ' <i class="q2">(' . Lang::item('heroic') . ')</i>' : '') . '<br />'; } if (!$nClasses || !$this->getField('contentGroup')) { $x .= Lang::itemset('types', $this->getField('type')) . '<br />'; } if ($bonuses = $this->getBonuses()) { $x .= '<span>'; foreach ($bonuses as $b) { $x .= '<br /><span class=\\"q13\\">' . $b['bonus'] . ' ' . Lang::itemset('_pieces') . Lang::main('colon') . '</span>' . Util::jsEscape($b['desc']); } $x .= '</span>'; } $x .= '</td></tr></table>'; return $x; }
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&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&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('/([^;]*)( <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; }
protected function generateContent() { $this->addJS('?data=zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']); $_cat = $this->subject->getField('typeCat'); $redButtons = array(BUTTON_LINKS => ['color' => 'ff71d5ff', 'linkId' => Util::$typeStrings[TYPE_SPELL] . ':' . $this->typeId], BUTTON_VIEW3D => false, BUTTON_WOWHEAD => true); /***********/ /* Infobox */ /***********/ $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); // level if (!in_array($_cat, [-5, -6])) { if ($_ = $this->subject->getField('talentLevel')) { $infobox[] = in_array($_cat, [-2, 7, -13]) ? sprintf(Lang::game('reqLevel'), $_) : Lang::game('level') . Lang::main('colon') . $_; } else { if ($_ = $this->subject->getField('spellLevel')) { $infobox[] = in_array($_cat, [-2, 7, -13]) ? sprintf(Lang::game('reqLevel'), $_) : Lang::game('level') . Lang::main('colon') . $_; } } } // races if ($_ = Lang::getRaceString($this->subject->getField('reqRaceMask'), $__, $jsg, $n, false)) { if ($_ != Lang::game('ra', 0)) { $this->extendGlobalIds(TYPE_RACE, $jsg); $t = $n == 1 ? Lang::game('race') : Lang::game('races'); $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_; } } // classes if ($_ = Lang::getClassString($this->subject->getField('reqClassMask'), $jsg, $n, false)) { $this->extendGlobalIds(TYPE_CLASS, $jsg); $t = $n == 1 ? Lang::game('class') : Lang::game('classes'); $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_; } // spell focus if ($_ = $this->subject->getField('spellFocusObject')) { $bar = DB::Aowow()->selectRow('SELECT * FROM ?_spellfocusobject WHERE id = ?d', $_); $focus = new GameObjectList(array(['spellFocusId', $_], 1)); $infobox[] = Lang::game('requires2') . ' ' . ($focus->error ? Util::localizedString($bar, 'name') : '[url=?object=' . $focus->id . ']' . Util::localizedString($bar, 'name') . '[/url]'); } // primary & secondary trades if (in_array($_cat, [9, 11])) { // skill if ($_ = $this->subject->getField('skillLines')[0]) { $rSkill = new SkillList(array(['id', $_])); if (!$rSkill->error) { $this->extendGlobalData($rSkill->getJSGlobals()); $bar = sprintf(Lang::game('requires'), ' [skill=' . $rSkill->id . ']'); if ($_ = $this->subject->getField('learnedAt')) { $bar .= ' (' . $_ . ')'; } $infobox[] = $bar; } } // specialization if ($_ = $this->subject->getField('reqSpellId')) { $rSpell = new SpellList(array(['id', $_])); if (!$rSpell->error) { $this->extendGlobalData($rSpell->getJSGlobals()); $infobox[] = Lang::game('requires2') . ' [spell=' . $rSpell->id . '][/li]'; } } // difficulty if ($_ = $this->subject->getColorsForCurrent()) { $bar = []; for ($i = 0; $i < 4; $i++) { if ($_[$i]) { $bar[] = '[color=r' . ($i + 1) . ']' . $_[$i] . '[/color]'; } } $infobox[] = Lang::game('difficulty') . Lang::main('colon') . implode(' ', $bar); } } // accquisition.. 10: starter spell; 7: discovery if (isset($this->subject->sources[$this->subject->id][10])) { $infobox[] = Lang::spell('starter'); } else { if (isset($this->subject->sources[$this->subject->id][7])) { $infobox[] = Lang::spell('discovered'); } } // training cost if ($cost = $this->subject->getField('trainingCost')) { $infobox[] = Lang::spell('trainingCost') . Lang::main('colon') . '[money=' . $cost . '][/li]'; } // used in mode foreach ($this->difficulties as $n => $id) { if ($id == $this->typeId) { // "Mode" seems to be multilingual acceptable $infobox[] = 'Mode' . Lang::main('colon') . Lang::game('modes', $n); } } $effects = $this->createEffects($infobox, $redButtons); // spell script if (User::isInGroup(U_GROUP_STAFF)) { if ($_ = DB::World()->selectCell('SELECT ScriptName FROM spell_script_names WHERE ABS(spell_id) = ?d', $this->firstRank)) { $infobox[] = 'Script' . Lang::main('colon') . $_; } } $infobox = $infobox ? '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]' : ''; // append glyph symbol if available $glyphId = 0; for ($i = 1; $i < 4; $i++) { if ($this->subject->getField('effect' . $i . 'Id') == 74) { $glyphId = $this->subject->getField('effect' . $i . 'MiscValue'); } } if ($_ = DB::Aowow()->selectCell('SELECT si.iconString FROM ?_glyphproperties gp JOIN ?_icons si ON gp.iconId = si.id WHERE gp.spellId = ?d { OR gp.id = ?d }', $this->typeId, $glyphId ?: DBSIMPLE_SKIP)) { if (file_exists('static/images/wow/Interface/Spellbook/' . $_ . '.png')) { $infobox .= '[img src=' . STATIC_URL . '/images/wow/Interface/Spellbook/' . $_ . '.png border=0 float=center margin=15]'; } } /****************/ /* Main Content */ /****************/ $this->reagents = $this->createReagentList(); $this->scaling = $this->createScalingData(); $this->items = $this->createRequiredItems(); $this->tools = $this->createTools(); $this->effects = $effects; $this->infobox = $infobox; $this->powerCost = $this->subject->createPowerCostForCurrent(); $this->castTime = $this->subject->createCastTimeForCurrent(false, false); $this->name = $this->subject->getField('name', true); $this->headIcons = [$this->subject->getField('iconString'), $this->subject->getField('stackAmount')]; $this->level = $this->subject->getField('spellLevel'); $this->rangeName = $this->subject->getField('rangeText', true); $this->range = $this->subject->getField('rangeMaxHostile'); $this->gcd = Util::formatTime($this->subject->getField('startRecoveryTime')); $this->gcdCat = null; // todo (low): nyi; find out how this works [n/a; normal; ..] $this->school = [Util::asHex($this->subject->getField('schoolMask')), Lang::getMagicSchools($this->subject->getField('schoolMask'))]; $this->dispel = $this->subject->getField('dispelType') ? Lang::game('dt', $this->subject->getField('dispelType')) : null; $this->mechanic = $this->subject->getField('mechanic') ? Lang::game('me', $this->subject->getField('mechanic')) : null; $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE; $this->redButtons = $redButtons; // minRange exists.. prepend if ($_ = $this->subject->getField('rangeMinHostile')) { $this->range = $_ . ' - ' . $this->range; } if ($this->subject->getField('attributes2') & 0x80000) { $this->stances = Lang::getStances($this->subject->getField('stanceMask')); } if (($_ = $this->subject->getField('recoveryTime')) && $_ > 0) { $this->cooldown = Util::formatTime($_); } if (($_ = $this->subject->getField('duration')) && $_ > 0) { $this->duration = Util::formatTime($_); } // factionchange-equivalent if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_spells WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId)) { $altSpell = new SpellList(array(['id', abs($pendant)])); if (!$altSpell->error) { $this->transfer = sprintf(Lang::spell('_transfer'), $altSpell->id, 1, $altSpell->getField('iconString'), $altSpell->getField('name', true), $pendant > 0 ? 'alliance' : 'horde', $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2)); } } /**************/ /* Extra Tabs */ /**************/ $j = [null, 'A', 'B', 'C']; // tab: abilities [of shapeshift form] for ($i = 1; $i < 4; $i++) { if ($this->subject->getField('effect' . $i . 'AuraId') != 36) { continue; } $formSpells = DB::Aowow()->selectRow('SELECT spellId1, spellId2, spellId3, spellId4, spellId5, spellId6, spellId7, spellId8 FROM ?_shapeshiftforms WHERE id = ?d', $this->subject->getField('effect' . $i . 'MiscValue')); if (!$formSpells) { continue; } $abilities = new SpellList(array(['id', $formSpells])); if (!$abilities->error) { if (!$abilities->hasSetFields(['skillLines'])) { $abH = "\$['skill']"; } $this->lvTabs[] = array('file' => 'spell', 'data' => $abilities->getListviewData(), 'params' => array('id' => 'controlledabilities', 'name' => '$LANG.tab_controlledabilities', 'visibleCols' => "\$['level']", 'hiddenCols' => isset($abH) ? $abH : null)); $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF)); } } // tab: modifies $this $sub = ['OR']; $conditions = [['s.typeCat', [0, -9, -8], '!'], ['s.spellFamilyId', $this->subject->getField('spellFamilyId')], &$sub]; for ($i = 1; $i < 4; $i++) { // Flat Mods (107), Pct Mods (108), No Reagent Use (256) .. include dummy..? (4) if (!in_array($this->subject->getField('effect' . $i . 'AuraId'), [107, 108, 256, 286])) { continue; } $m1 = $this->subject->getField('effect1SpellClassMask' . $j[$i]); $m2 = $this->subject->getField('effect2SpellClassMask' . $j[$i]); $m3 = $this->subject->getField('effect3SpellClassMask' . $j[$i]); if (!$m1 && !$m2 && !$m3) { continue; } $sub[] = ['s.spellFamilyFlags1', $m1, '&']; $sub[] = ['s.spellFamilyFlags2', $m2, '&']; $sub[] = ['s.spellFamilyFlags3', $m3, '&']; } if (count($sub) > 1) { $modSpells = new SpellList($conditions); if (!$modSpells->error) { if (!$modSpells->hasSetFields(['skillLines'])) { $msH = "\$['skill']"; } $this->lvTabs[] = array('file' => 'spell', 'data' => $modSpells->getListviewData(), 'params' => array('id' => 'modifies', 'name' => '$LANG.tab_modifies', 'visibleCols' => "\$['level']", 'hiddenCols' => isset($msH) ? $msH : null)); $this->extendGlobalData($modSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } } // tab: modified by $this $sub = ['OR']; $conditions = [['s.spellFamilyId', $this->subject->getField('spellFamilyId')], &$sub]; for ($i = 1; $i < 4; $i++) { $m1 = $this->subject->getField('spellFamilyFlags1'); $m2 = $this->subject->getField('spellFamilyFlags2'); $m3 = $this->subject->getField('spellFamilyFlags3'); if (!$m1 && !$m2 && !$m3) { continue; } $sub[] = array('AND', ['s.effect' . $i . 'AuraId', [107, 108, 256, 286]], ['OR', ['s.effect1SpellClassMask' . $j[$i], $m1, '&'], ['s.effect2SpellClassMask' . $j[$i], $m2, '&'], ['s.effect3SpellClassMask' . $j[$i], $m3, '&']]); } if (count($sub) > 1) { $modsSpell = new SpellList($conditions); if (!$modsSpell->error) { if (!$modsSpell->hasSetFields(['skillLines'])) { $mbH = "\$['skill']"; } $this->lvTabs[] = array('file' => 'spell', 'data' => $modsSpell->getListviewData(), 'params' => array('id' => 'modified-by', 'name' => '$LANG.tab_modifiedby', 'visibleCols' => "\$['level']", 'hiddenCols' => isset($mbH) ? $mbH : null)); $this->extendGlobalData($modsSpell->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } } // tab: see also $conditions = array(['s.schoolMask', $this->subject->getField('schoolMask')], ['s.effect1Id', $this->subject->getField('effect1Id')], ['s.effect2Id', $this->subject->getField('effect2Id')], ['s.effect3Id', $this->subject->getField('effect3Id')], ['s.id', $this->subject->id, '!'], ['s.name_loc' . User::$localeId, $this->subject->getField('name', true)]); $saSpells = new SpellList($conditions); if (!$saSpells->error) { $data = $saSpells->getListviewData(); if ($this->difficulties) { $saE = '$[Listview.extraCols.mode]'; foreach ($data as $id => &$d) { $d['modes'] = ['mode' => 0]; if ($this->difficulties[0] == $id) { if (!$this->difficulties[2] && !$this->difficulties[3]) { $d['modes']['mode'] |= 0x2; } else { $d['modes']['mode'] |= 0x8; } } if ($this->difficulties[1] == $id) { if (!$this->difficulties[2] && !$this->difficulties[3]) { $d['modes']['mode'] |= 0x1; } else { $d['modes']['mode'] |= 0x10; } } if ($this->difficulties[2] == $id) { // b0100000 $d['modes']['mode'] |= 0x20; } if ($this->difficulties[3] == $id) { // b1000000 $d['modes']['mode'] |= 0x40; } } } if (!$saSpells->hasSetFields(['skillLines'])) { $saH = "\$['skill']"; } $this->lvTabs[] = array('file' => 'spell', 'data' => $data, 'params' => array('id' => 'see-also', 'name' => '$LANG.tab_seealso', 'visibleCols' => "\$['level']", 'extraCols' => isset($saE) ? $saE : null, 'hiddenCols' => isset($saH) ? $saH : null)); $this->extendGlobalData($saSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } // tab: used by - itemset $conditions = array('OR', ['spell1', $this->subject->id], ['spell2', $this->subject->id], ['spell3', $this->subject->id], ['spell4', $this->subject->id], ['spell5', $this->subject->id], ['spell6', $this->subject->id], ['spell7', $this->subject->id], ['spell8', $this->subject->id]); $ubSets = new ItemsetList($conditions); if (!$ubSets->error) { $this->lvTabs[] = array('file' => 'itemset', 'data' => $ubSets->getListviewData(), 'params' => array('id' => 'used-by-itemset', 'name' => '$LANG.tab_usedby')); $this->extendGlobalData($ubSets->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } // tab: used by - item $conditions = array('OR', ['AND', ['spellTrigger1', 6, '!'], ['spellId1', $this->subject->id]], ['AND', ['spellTrigger2', 6, '!'], ['spellId2', $this->subject->id]], ['AND', ['spellTrigger3', 6, '!'], ['spellId3', $this->subject->id]], ['AND', ['spellTrigger4', 6, '!'], ['spellId4', $this->subject->id]], ['AND', ['spellTrigger5', 6, '!'], ['spellId5', $this->subject->id]]); $ubItems = new ItemList($conditions); if (!$ubItems->error) { $this->lvTabs[] = array('file' => 'item', 'data' => $ubItems->getListviewData(), 'params' => array('id' => 'used-by-item', 'name' => '$LANG.tab_usedby')); $this->extendGlobalData($ubItems->getJSGlobals(GLOBALINFO_SELF)); } // tab: used by - object $conditions = array('OR', ['onUseSpell', $this->subject->id], ['onSuccessSpell', $this->subject->id], ['auraSpell', $this->subject->id], ['triggeredSpell', $this->subject->id]); $ubObjects = new GameObjectList($conditions); if (!$ubObjects->error) { $this->lvTabs[] = array('file' => 'object', 'data' => $ubObjects->getListviewData(), 'params' => array('id' => 'used-by-object', 'name' => '$LANG.tab_usedby')); $this->extendGlobalData($ubObjects->getJSGlobals()); } // tab: criteria of $conditions = array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL]], ['ac.value1', $this->typeId]); $coAchievemnts = new AchievementList($conditions); if (!$coAchievemnts->error) { $this->lvTabs[] = array('file' => 'achievement', 'data' => $coAchievemnts->getListviewData(), 'params' => array('id' => 'criteria-of', 'name' => '$LANG.tab_criteriaof')); $this->extendGlobalData($coAchievemnts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } // tab: contains // spell_loot_template & skill_extra_item_template $extraItem = DB::World()->selectRow('SELECT * FROM skill_extra_item_template WHERE spellid = ?d', $this->subject->id); $spellLoot = new Loot(); if ($spellLoot->getByContainer(LOOT_SPELL, $this->subject->id) || $extraItem) { $this->extendGlobalData($spellLoot->jsGlobals); $lv = $spellLoot->getResult(); $extraCols = $spellLoot->extraCols; $extraCols[] = 'Listview.extraCols.percent'; if ($extraItem && $this->subject->canCreateItem()) { $foo = $this->subject->relItems->getListviewData(); for ($i = 1; $i < 4; $i++) { if (($bar = $this->subject->getField('effect' . $i . 'CreateItemId')) && isset($foo[$bar])) { $lv[$bar] = $foo[$bar]; $lv[$bar]['percent'] = $extraItem['additionalCreateChance']; $lv[$bar]['condition'][0][$this->typeId][] = [[CND_SPELL, $extraItem['requiredSpecialization']]]; $this->extendGlobalIds(TYPE_SPELL, $extraItem['requiredSpecialization']); $extraCols[] = 'Listview.extraCols.condition'; if ($max = $extraItem['additionalMaxNum']) { $lv[$bar]['mincount'] = 1; $lv[$bar]['maxcount'] = $max; } break; // skill_extra_item_template can only contain 1 item } } } $this->lvTabs[] = array('file' => 'item', 'data' => $lv, 'params' => array('name' => '$LANG.tab_contains', 'id' => 'contains', 'hiddenCols' => "\$['side', 'slot', 'source', 'reqlevel']", 'extraCols' => '$[' . implode(', ', $extraCols) . ']')); } // tab: exclusive with if ($this->firstRank) { $linkedSpells = DB::World()->selectCol('SELECT IF(sg2.spell_id < 0, sg2.id, sg2.spell_id) AS ARRAY_KEY, IF(sg2.spell_id < 0, sg2.spell_id, sr.stack_rule) FROM spell_group sg1 JOIN spell_group sg2 ON (sg1.id = sg2.id OR sg1.id = -sg2.spell_id) AND sg1.spell_id != sg2.spell_id LEFT JOIN spell_group_stack_rules sr ON sg1.id = sr.group_id WHERE sg1.spell_id = ?d', $this->firstRank); if ($linkedSpells) { $extraSpells = []; foreach ($linkedSpells as $k => $v) { if ($v > 0) { continue; } $extraSpells += DB::World()->selectCol('SELECT sg2.spell_id AS ARRAY_KEY, sr.stack_rule FROM spell_group sg1 JOIN spell_group sg2 ON sg2.id = -sg1.spell_id AND sg2.spell_id != ?d LEFT JOIN spell_group_stack_rules sr ON sg1.id = sr.group_id WHERE sg1.id = ?d', $this->firstRank, $k); unset($linkedSpells[$k]); } $groups = $linkedSpells + $extraSpells; $stacks = new SpellList(array(['s.id', array_keys($groups)])); if (!$stacks->error) { $data = $stacks->getListviewData(); foreach ($data as $k => $d) { $data[$k]['stackRule'] = $groups[$k]; } if (!$stacks->hasSetFields(['skillLines'])) { $sH = "\$['skill']"; } $this->lvTabs[] = array('file' => 'spell', 'data' => $data, 'params' => array('id' => 'spell-group-stack', 'name' => 'Stack Group', 'visibleCols' => "\$['stackRules']", 'hiddenCols' => isset($sH) ? $sH : null)); $this->extendGlobalData($stacks->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } } } // tab: linked with $rows = DB::World()->select(' SELECT spell_trigger AS `trigger`, spell_effect AS effect, type, IF(ABS(spell_effect) = ?d, ABS(spell_trigger), ABS(spell_effect)) AS related FROM spell_linked_spell WHERE ABS(spell_effect) = ?d OR ABS(spell_trigger) = ?d', $this->typeId, $this->typeId, $this->typeId); $related = []; foreach ($rows as $row) { $related[] = $row['related']; } if ($related) { $linked = new SpellList(array(['s.id', $related])); } if (isset($linked) && !$linked->error) { $lv = $linked->getListviewData(); $data = []; foreach ($rows as $r) { foreach ($lv as $dk => $d) { if ($r['related'] != $dk) { continue; } $lv[$dk]['linked'] = [$r['trigger'], $r['effect'], $r['type']]; $data[] = $lv[$dk]; break; } } $this->lvTabs[] = array('file' => 'spell', 'data' => $data, 'params' => array('id' => 'spell-link', 'name' => 'Linked with', 'hiddenCols' => "\$['skill', 'name']", 'visibleCols' => "\$['linkedTrigger', 'linkedEffect']")); $this->extendGlobalData($linked->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } // tab: triggered by $conditions = array('OR', ['AND', ['OR', ['effect1Id', SpellList::$effects['trigger']], ['effect1AuraId', SpellList::$auras['trigger']]], ['effect1TriggerSpell', $this->subject->id]], ['AND', ['OR', ['effect2Id', SpellList::$effects['trigger']], ['effect2AuraId', SpellList::$auras['trigger']]], ['effect2TriggerSpell', $this->subject->id]], ['AND', ['OR', ['effect3Id', SpellList::$effects['trigger']], ['effect3AuraId', SpellList::$auras['trigger']]], ['effect3TriggerSpell', $this->subject->id]]); $trigger = new SpellList($conditions); if (!$trigger->error) { $this->lvTabs[] = array('file' => 'spell', 'data' => $trigger->getListviewData(), 'params' => array('id' => 'triggered-by', 'name' => '$LANG.tab_triggeredby')); $this->extendGlobalData($trigger->getJSGlobals(GLOBALINFO_SELF)); } // tab: used by - creature // SMART_SCRIPT_TYPE_CREATURE = 0; SMART_ACTION_CAST = 11; SMART_ACTION_ADD_AURA = 75; SMART_ACTION_INVOKER_CAST = 85; SMART_ACTION_CROSS_CAST = 86 $conditions = array('OR', ['spell1', $this->typeId], ['spell2', $this->typeId], ['spell3', $this->typeId], ['spell4', $this->typeId], ['spell5', $this->typeId], ['spell6', $this->typeId], ['spell7', $this->typeId], ['spell8', $this->typeId]); if ($_ = DB::World()->selectCol('SELECT entryOrGUID FROM smart_scripts WHERE entryorguid > 0 AND source_type = 0 AND action_type IN (11, 75, 85, 86) AND action_param1 = ?d', $this->typeId)) { $conditions[] = ['id', $_]; } $ubCreature = new CreatureList($conditions); if (!$ubCreature->error) { $this->lvTabs[] = array('file' => 'creature', 'data' => $ubCreature->getListviewData(), 'params' => array('id' => 'used-by-npc', 'name' => '$LANG.tab_usedby')); $this->extendGlobalData($ubCreature->getJSGlobals(GLOBALINFO_SELF)); } // tab: zone if ($areas = DB::World()->select('SELECT * FROM spell_area WHERE spell = ?d', $this->typeId)) { $zones = new ZoneList(array(['id', array_column($areas, 'area')])); if (!$zones->error) { $lvZones = $zones->getListviewData(); $this->extendGlobalData($zones->getJSGlobals()); $lv = []; $parents = []; $extra = false; foreach ($areas as $a) { if (empty($lvZones[$a['area']])) { continue; } $condition = []; if ($a['aura_spell']) { $this->extendGlobalIds(TYPE_SPELL, abs($a['aura_spell'])); $condition[0][$this->typeId][] = [[$a['aura_spell'] > 0 ? CND_AURA : -CND_AURA, abs($a['aura_spell'])]]; } if ($a['quest_start']) { $this->extendGlobalIds(TYPE_QUEST, $a['quest_start']); $group = []; for ($i = 0; $i < 7; $i++) { if (!($a['quest_start_status'] & 1 << $i)) { continue; } if ($i == 0) { $group[] = [CND_QUEST_NONE, $a['quest_start']]; } else { if ($i == 1) { $group[] = [CND_QUEST_COMPLETE, $a['quest_start']]; } else { if ($i == 3) { $group[] = [CND_QUESTTAKEN, $a['quest_start']]; } else { if ($i == 6) { $group[] = [CND_QUESTREWARDED, $a['quest_start']]; } } } } } if ($group) { $condition[0][$this->typeId][] = $group; } } if ($a['quest_end'] && $a['quest_end'] != $a['quest_start']) { $this->extendGlobalIds(TYPE_QUEST, $a['quest_end']); $group = []; for ($i = 0; $i < 7; $i++) { if (!($a['quest_end_status'] & 1 << $i)) { continue; } if ($i == 0) { $group[] = [-CND_QUEST_NONE, $a['quest_end']]; } else { if ($i == 1) { $group[] = [-CND_QUEST_COMPLETE, $a['quest_end']]; } else { if ($i == 3) { $group[] = [-CND_QUESTTAKEN, $a['quest_end']]; } else { if ($i == 6) { $group[] = [-CND_QUESTREWARDED, $a['quest_end']]; } } } } } if ($group) { $condition[0][$this->typeId][] = $group; } } if ($a['racemask']) { $foo = []; for ($i = 0; $i < 11; $i++) { if ($a['racemask'] & 1 << $i) { $foo[] = $i + 1; } } $this->extendGlobalIds(TYPE_RACE, $foo); $condition[0][$this->typeId][] = [[CND_RACE, $a['racemask']]]; } if ($a['gender'] != 2) { // 2: both $condition[0][$this->typeId][] = [[CND_GENDER, $a['gender'] + 1]]; } $row = $lvZones[$a['area']]; if ($condition) { $extra = true; $row = array_merge($row, ['condition' => $condition]); } // merge subzones, into one row, if: conditions match && parentZone is shared if ($p = $zones->getEntry($a['area'])['parentArea']) { $parents[] = $p; $row['parentArea'] = $p; $row['subzones'] = [$a['area']]; } else { $row['parentArea'] = 0; } $set = false; foreach ($lv as &$v) { if ($v['parentArea'] != $row['parentArea'] && $v['id'] != $row['parentArea']) { continue; } if (empty($v['condition']) xor empty($row['condition'])) { continue; } if (!empty($row['condition']) && !empty($v['condition']) && $v['condition'] != $row['condition']) { continue; } if (!$row['parentArea'] && $v['id'] != $row['parentArea']) { continue; } $set = true; $v['subzones'][] = $row['id']; break; } // add self as potential subzone; IF we are a parentZone without added children, we get filtered in JScript if (!$set) { $row['subzones'] = [$row['id']]; $lv[] = $row; } } // overwrite lvData with parent-lvData (condition and subzones are kept) if ($parents) { $parents = (new ZoneList(array(['id', $parents])))->getListviewData(); foreach ($lv as &$_) { if (isset($parents[$_['parentArea']])) { $_ = array_merge($_, $parents[$_['parentArea']]); } } } $this->lvTabs[] = array('file' => 'zone', 'data' => $lv, 'params' => array('extraCols' => $extra ? '$[Listview.extraCols.condition]' : null, 'hiddenCols' => $extra ? "\$['instancetype']" : null)); } } // tab: teaches if ($ids = Util::getTaughtSpells($this->subject)) { $teaches = new SpellList(array(['id', $ids])); if (!$teaches->error) { $this->extendGlobalData($teaches->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $vis = ['level', 'schools']; $hid = []; if (!$teaches->hasSetFields(['skillLines'])) { $hid[] = 'skill'; } foreach ($teaches->iterate() as $__) { if (!$teaches->canCreateItem()) { continue; } $vis[] = 'reagents'; break; } $this->lvTabs[] = array('file' => 'spell', 'data' => $teaches->getListviewData(), 'params' => array('id' => 'teaches-spell', 'name' => '$LANG.tab_teaches', 'visibleCols' => '$' . Util::toJSON($vis), 'hiddenCols' => $hid ? '$' . Util::toJSON($hid) : null)); } } // tab: taught by npc (source:6 => trainer) if (!empty($this->subject->sources[$this->typeId][6])) { $src = $this->subject->sources[$this->typeId][6]; $list = []; if (count($src) == 1 && $src[0] == 1) { $tt = null; // Professions if (in_array($_cat, [9, 11]) && isset(Util::$trainerTemplates[TYPE_SKILL][$this->subject->getField('skillLines')[0]])) { $tt = Util::$trainerTemplates[TYPE_SKILL][$this->subject->getField('skillLines')[0]]; } else { if ($_cat == 7 && $this->subject->getField('reqClassMask')) { $clId = log($this->subject->getField('reqClassMask'), 2) + 1; if (intVal($clId) == $clId) { // only one class was set, so float == int if (isset(Util::$trainerTemplates[TYPE_CLASS][$clId])) { $tt = Util::$trainerTemplates[TYPE_CLASS][$clId]; } } } } if ($tt) { $list = DB::World()->selectCol('SELECT DISTINCT ID FROM npc_trainer WHERE SpellID IN (?a) AND ID < 200000', $tt); } else { $mask = 0; foreach (Util::$skillLineMask[-3] as $idx => $pair) { if ($pair[1] == $this->typeId) { $mask |= 1 << $idx; } } $list = DB::World()->selectCol(' SELECT IF(t1.ID > 200000, t2.ID, t1.ID) FROM npc_trainer t1 LEFT JOIN npc_trainer t2 ON t2.SpellID = -t1.ID WHERE t1.SpellID = ?d', $this->typeId); } } else { if ($src) { $list = array_values($src); } } if ($list) { $tbTrainer = new CreatureList(array(CFG_SQL_LIMIT_NONE, ['ct.id', $list], ['s.guid', NULL, '!'], ['ct.npcflag', 0x10, '&'])); if (!$tbTrainer->error) { $this->extendGlobalData($tbTrainer->getJSGlobals()); $this->lvTabs[] = array('file' => 'creature', 'data' => $tbTrainer->getListviewData(), 'params' => array('id' => 'taught-by-npc', 'name' => '$LANG.tab_taughtby')); } } } // tab: taught by spell $conditions = array('OR', ['AND', ['effect1Id', SpellList::$effects['teach']], ['effect1TriggerSpell', $this->subject->id]], ['AND', ['effect2Id', SpellList::$effects['teach']], ['effect2TriggerSpell', $this->subject->id]], ['AND', ['effect3Id', SpellList::$effects['teach']], ['effect3TriggerSpell', $this->subject->id]]); $tbSpell = new SpellList($conditions); $tbsData = []; if (!$tbSpell->error) { $tbsData = $tbSpell->getListviewData(); $this->lvTabs[] = array('file' => 'spell', 'data' => $tbsData, 'params' => array('id' => 'taught-by-spell', 'name' => '$LANG.tab_taughtby')); $this->extendGlobalData($tbSpell->getJSGlobals(GLOBALINFO_SELF)); } // tab: taught by quest $conditions = ['OR', ['sourceSpellId', $this->typeId], ['rewardSpell', $this->typeId]]; if ($tbsData) { $conditions[] = ['rewardSpell', array_keys($tbsData)]; if (User::isInGroup(U_GROUP_EMPLOYEE)) { $conditions[] = ['rewardSpellCast', array_keys($tbsData)]; } } if (User::isInGroup(U_GROUP_EMPLOYEE)) { $conditions[] = ['rewardSpellCast', $this->typeId]; } $tbQuest = new QuestList($conditions); if (!$tbQuest->error) { $this->lvTabs[] = array('file' => 'quest', 'data' => $tbQuest->getListviewData(), 'params' => array('id' => 'reward-from-quest', 'name' => '$LANG.tab_rewardfrom')); $this->extendGlobalData($tbQuest->getJSGlobals()); } // tab: taught by item (i'd like to precheck $this->subject->sources, but there is no source:item only complicated crap like "drop" and "vendor") $conditions = array('OR', ['AND', ['spellTrigger1', 6], ['spellId1', $this->subject->id]], ['AND', ['spellTrigger2', 6], ['spellId2', $this->subject->id]], ['AND', ['spellTrigger3', 6], ['spellId3', $this->subject->id]], ['AND', ['spellTrigger4', 6], ['spellId4', $this->subject->id]], ['AND', ['spellTrigger5', 6], ['spellId5', $this->subject->id]]); $tbItem = new ItemList($conditions); if (!$tbItem->error) { $this->lvTabs[] = array('file' => 'item', 'data' => $tbItem->getListviewData(), 'params' => array('id' => 'taught-by-item', 'name' => '$LANG.tab_taughtby')); $this->extendGlobalData($tbItem->getJSGlobals(GLOBALINFO_SELF)); } // tab: enchantments $conditions = array('OR', ['AND', ['type1', [1, 3, 7]], ['object1', $this->typeId]], ['AND', ['type2', [1, 3, 7]], ['object2', $this->typeId]], ['AND', ['type3', [1, 3, 7]], ['object3', $this->typeId]]); $enchList = new EnchantmentList($conditions); if (!$enchList->error) { $this->lvTabs[] = array('file' => 'enchantment', 'data' => $enchList->getListviewData(), 'params' => []); $this->extendGlobalData($enchList->getJSGlobals()); } // find associated NPC, Item and merge results // taughtbypets (unused..?) // taughtbyquest (usually the spell casted as quest reward teaches something; exclude those seplls from taughtBySpell) // taughtbytrainers // taughtbyitem // tab: conditions $sc = Util::getServerConditions([CND_SRC_SPELL_LOOT_TEMPLATE, CND_SRC_SPELL_IMPLICIT_TARGET, CND_SRC_SPELL, CND_SRC_SPELL_CLICK_EVENT, CND_SRC_VEHICLE_SPELL, CND_SRC_SPELL_PROC], null, $this->typeId); if (!empty($sc[0])) { $this->extendGlobalData($sc[1]); $tab = "<script type=\"text/javascript\">\n" . "var markup = ConditionList.createTab(" . Util::toJSON($sc[0]) . ");\n" . "Markup.printHtml(markup, 'tab-conditions', { allow: Markup.CLASS_STAFF })" . "</script>"; $this->lvTabs[] = array('file' => null, 'data' => $tab, 'params' => array('id' => 'conditions', 'name' => '$LANG.requires')); } }
protected function generateContent() { $_level = $this->subject->getField('level'); $_minLevel = $this->subject->getField('minLevel'); $_flags = $this->subject->getField('flags'); $_specialFlags = $this->subject->getField('specialFlags'); $_side = Util::sideByRaceMask($this->subject->getField('reqRaceMask')); /***********/ /* Infobox */ /***********/ $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); // event (todo: assign eventData) if ($_ = $this->subject->getField('eventId')) { $this->extendGlobalIds(TYPE_WORLDEVENT, $_); $infobox[] = Lang::game('eventShort') . Lang::main('colon') . '[event=' . $_ . ']'; } // level if ($_level > 0) { $infobox[] = Lang::game('level') . Lang::main('colon') . $_level; } // reqlevel if ($_minLevel) { $lvl = $_minLevel; if ($_ = $this->subject->getField('maxLevel')) { $lvl .= ' - ' . $_; } $infobox[] = sprintf(Lang::game('reqLevel'), $lvl); } // loremaster (i dearly hope those flags cover every case...) if ($this->subject->getField('zoneOrSortBak') > 0 && !$this->subject->isRepeatable()) { $conditions = array(['ac.type', ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE], ['ac.value1', $this->subject->getField('zoneOrSortBak')], ['a.faction', $_side, '&']); $loremaster = new AchievementList($conditions); $this->extendGlobalData($loremaster->getJSGlobals(GLOBALINFO_SELF)); switch ($loremaster->getMatches()) { case 0: break; case 1: $infobox[] = Lang::quest('loremaster') . Lang::main('colon') . '[achievement=' . $loremaster->id . ']'; break; default: $lm = Lang::quest('loremaster') . Lang::main('colon') . '[ul]'; foreach ($loremaster->iterate() as $id => $__) { $lm .= '[li][achievement=' . $id . '][/li]'; } $infobox[] = $lm . '[/ul]'; break; } } // type (maybe expand uppon?) $_ = []; if ($_flags & QUEST_FLAG_DAILY) { $_[] = Lang::quest('daily'); } else { if ($_flags & QUEST_FLAG_WEEKLY) { $_[] = Lang::quest('weekly'); } else { if ($_specialFlags & QUEST_FLAG_SPECIAL_MONTHLY) { $_[] = Lang::quest('monthly'); } } } if ($t = $this->subject->getField('type')) { $_[] = Lang::quest('questInfo', $t); } if ($_) { $infobox[] = Lang::game('type') . Lang::main('colon') . implode(' ', $_); } // side $_ = Lang::main('side') . Lang::main('colon'); switch ($_side) { case 3: $infobox[] = $_ . Lang::game('si', 3); break; case 2: $infobox[] = $_ . '[span class=icon-horde]' . Lang::game('si', 2) . '[/span]'; break; case 1: $infobox[] = $_ . '[span class=icon-alliance]' . Lang::game('si', 1) . '[/span]'; break; } // races if ($_ = Lang::getRaceString($this->subject->getField('reqRaceMask'), $__, $jsg, $n, false)) { $this->extendGlobalIds(TYPE_RACE, $jsg); $t = $n == 1 ? Lang::game('race') : Lang::game('races'); $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_; } // classes if ($_ = Lang::getClassString($this->subject->getField('reqClassMask'), $jsg, $n, false)) { $this->extendGlobalIds(TYPE_CLASS, $jsg); $t = $n == 1 ? Lang::game('class') : Lang::game('classes'); $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $_; } // profession / skill if ($_ = $this->subject->getField('reqSkillId')) { $this->extendGlobalIds(TYPE_SKILL, $_); $sk = '[skill=' . $_ . ']'; if ($_ = $this->subject->getField('reqSkillPoints')) { $sk .= ' (' . $_ . ')'; } $infobox[] = Lang::quest('profession') . Lang::main('colon') . $sk; } // timer if ($_ = $this->subject->getField('timeLimit')) { $infobox[] = Lang::quest('timer') . Lang::main('colon') . Util::formatTime($_ * 1000); } $startEnd = DB::Aowow()->select('SELECT * FROM ?_quests_startend WHERE questId = ?d', $this->typeId); // start $start = '[icon name=quest_start' . ($this->subject->isDaily() ? '_daily' : '') . ']' . Lang::event('start') . Lang::main('colon') . '[/icon]'; $s = []; foreach ($startEnd as $se) { if ($se['method'] & 0x1) { $this->extendGlobalIds($se['type'], $se['typeId']); $s[] = ($s ? '[span=invisible]' . $start . '[/span] ' : $start . ' ') . '[' . Util::$typeStrings[$se['type']] . '=' . $se['typeId'] . ']'; } } if ($s) { $infobox[] = implode('[br]', $s); } // end $end = '[icon name=quest_end' . ($this->subject->isDaily() ? '_daily' : '') . ']' . Lang::event('end') . Lang::main('colon') . '[/icon]'; $e = []; foreach ($startEnd as $se) { if ($se['method'] & 0x2) { $this->extendGlobalIds($se['type'], $se['typeId']); $e[] = ($e ? '[span=invisible]' . $end . '[/span] ' : $end . ' ') . '[' . Util::$typeStrings[$se['type']] . '=' . $se['typeId'] . ']'; } } if ($e) { $infobox[] = implode('[br]', $e); } // Repeatable if ($_flags & QUEST_FLAG_REPEATABLE || $_specialFlags & QUEST_FLAG_SPECIAL_REPEATABLE) { $infobox[] = Lang::quest('repeatable'); } // sharable | not sharable $infobox[] = $_flags & QUEST_FLAG_SHARABLE ? Lang::quest('sharable') : Lang::quest('notSharable'); // Keeps you PvP flagged if ($this->subject->isPvPEnabled()) { $infobox[] = Lang::quest('keepsPvpFlag'); } // difficulty (todo (low): formula unclear. seems to be [minLevel,] -4, -2, (level), +3, +(9 to 15)) if ($_level > 0) { $_ = []; // red if ($_minLevel && $_minLevel < $_level - 4) { $_[] = '[color=q10]' . $_minLevel . '[/color]'; } // orange if (!$_minLevel || $_minLevel < $_level - 2) { $_[] = '[color=r1]' . (!$_ && $_minLevel > $_level - 4 ? $_minLevel : $_level - 4) . '[/color]'; } // yellow $_[] = '[color=r2]' . (!$_ && $_minLevel > $_level - 2 ? $_minLevel : $_level - 2) . '[/color]'; // green $_[] = '[color=r3]' . ($_level + 3) . '[/color]'; // grey (is about +/-1 level off) $_[] = '[color=r4]' . ($_level + 3 + ceil(12 * $_level / MAX_LEVEL)) . '[/color]'; if ($_) { $infobox[] = Lang::game('difficulty') . Lang::main('colon') . implode('[small] [/small]', $_); } } $this->infobox = '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]'; /**********/ /* Series */ /**********/ // Quest Chain (are there cases where quests go in parallel?) $chain = array(array(array('side' => $_side, 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $this->typeId, 'name' => $this->name, '_next' => $this->subject->getField('nextQuestIdChain')))); $_ = $chain[0][0]; while ($_) { if ($_ = DB::Aowow()->selectRow('SELECT id AS typeId, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8, reqRaceMask FROM ?_quests WHERE nextQuestIdChain = ?d', $_['typeId'])) { $n = Util::localizedString($_, 'name'); array_unshift($chain, array(array('side' => Util::sideByRaceMask($_['reqRaceMask']), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $_['typeId'], 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n))); } } $_ = end($chain)[0]; while ($_) { if ($_ = DB::Aowow()->selectRow('SELECT id AS typeId, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8, reqRaceMask, nextQuestIdChain AS _next FROM ?_quests WHERE id = ?d', $_['_next'])) { $n = Util::localizedString($_, 'name'); array_push($chain, array(array('side' => Util::sideByRaceMask($_['reqRaceMask']), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $_['typeId'], 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n, '_next' => $_['_next']))); } } if (count($chain) > 1) { $this->series[] = [$chain, null]; } // todo (low): sensibly merge the following lists into 'series' $listGen = function ($cnd) { $chain = []; $list = new QuestList($cnd); if ($list->error) { return null; } foreach ($list->iterate() as $id => $__) { $n = $list->getField('name', true); $chain[] = array(array('side' => Util::sideByRaceMask($list->getField('reqRaceMask')), 'typeStr' => Util::$typeStrings[TYPE_QUEST], 'typeId' => $id, 'name' => strlen($n) > 40 ? substr($n, 0, 40) . '…' : $n)); } return $chain; }; $extraLists = array(['reqQ', array('OR', ['AND', ['nextQuestId', $this->typeId], ['exclusiveGroup', 0, '<']], ['AND', ['id', $this->subject->getField('prevQuestId')], ['nextQuestIdChain', $this->typeId, '!']])], ['reqOneQ', array(['exclusiveGroup', 0, '>'], ['nextQuestId', $this->typeId])], ['opensQ', array('OR', ['AND', ['prevQuestId', $this->typeId], ['id', $this->subject->getField('nextQuestIdChain'), '!']], ['id', $this->subject->getField('nextQuestId')])], ['closesQ', array(['exclusiveGroup', 0, '!'], ['exclusiveGroup', $this->subject->getField('exclusiveGroup')], ['id', $this->typeId, '!'])], ['enablesQ', array(['prevQuestId', -$this->typeId])], ['enabledByQ', array(['id', -$this->subject->getField('prevQuestId')])]); foreach ($extraLists as $el) { if ($_ = $listGen($el[1])) { $this->series[] = [$_, sprintf(Util::$dfnString, Lang::quest($el[0] . 'Desc'), Lang::quest($el[0]))]; } } /*******************/ /* Objectives List */ /*******************/ $this->objectiveList = []; $this->providedItem = []; // gather ids for lookup $olItems = $olNPCs = $olGOs = $olFactions = []; // items $olItems[0] = array($this->subject->getField('sourceItemId'), $this->subject->getField('sourceItemCount'), false); for ($i = 1; $i < 7; $i++) { $id = $this->subject->getField('reqItemId' . $i); $qty = $this->subject->getField('reqItemCount' . $i); if (!$id || !$qty) { continue; } $olItems[$i] = [$id, $qty, $id == $olItems[0][0]]; } if ($ids = array_column($olItems, 0)) { $olItemData = new ItemList(array(['id', $ids])); $this->extendGlobalData($olItemData->getJSGlobals(GLOBALINFO_SELF)); $providedRequired = false; foreach ($olItems as $i => list($itemId, $qty, $provided)) { if (!$i || !$itemId || !in_array($itemId, $olItemData->getFoundIDs())) { continue; } if ($provided) { $providedRequired = true; } $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_ITEM], 'id' => $itemId, 'name' => $olItemData->json[$itemId]['name'], 'qty' => $qty > 1 ? $qty : 0, 'quality' => 7 - $olItemData->json[$itemId]['quality'], 'extraText' => $provided ? ' (' . Lang::quest('provided') . ')' : ''); } // if providd item is not required by quest, list it below other requirements if (!$providedRequired && $olItems[0][0] && in_array($olItems[0][0], $olItemData->getFoundIDs())) { $this->providedItem = array('id' => $olItems[0][0], 'name' => $olItemData->json[$olItems[0][0]]['name'], 'qty' => $olItems[0][1] > 1 ? $olItems[0][1] : 0, 'quality' => 7 - $olItemData->json[$olItems[0][0]]['quality']); } } // creature or GO... for ($i = 1; $i < 5; $i++) { $id = $this->subject->getField('reqNpcOrGo' . $i); $qty = $this->subject->getField('reqNpcOrGoCount' . $i); $altTxt = $this->subject->getField('objectiveText' . $i, true); if ($id > 0 && $qty) { $olNPCs[$id] = [$qty, $altTxt, []]; } else { if ($id < 0 && $qty) { $olGOs[-$id] = [$qty, $altTxt]; } } } // .. creature kills if ($ids = array_keys($olNPCs)) { $olNPCData = new CreatureList(array('OR', ['id', $ids], ['killCredit1', $ids], ['killCredit2', $ids])); $this->extendGlobalData($olNPCData->getJSGlobals(GLOBALINFO_SELF)); // create proxy-references foreach ($olNPCData->iterate() as $id => $__) { if ($p = $olNPCData->getField('KillCredit1')) { if (isset($olNPCs[$p])) { $olNPCs[$p][2][$id] = $olNPCData->getField('name', true); } } if ($p = $olNPCData->getField('KillCredit2')) { if (isset($olNPCs[$p])) { $olNPCs[$p][2][$id] = $olNPCData->getField('name', true); } } } foreach ($olNPCs as $i => $pair) { if (!$i || !in_array($i, $olNPCData->getFoundIDs())) { continue; } $ol = array('typeStr' => Util::$typeStrings[TYPE_NPC], 'id' => $i, 'name' => $pair[1] ?: Util::localizedString($olNPCData->getEntry($i), 'name'), 'qty' => $pair[0] > 1 ? $pair[0] : 0, 'extraText' => $_specialFlags & QUEST_FLAG_SPECIAL_SPELLCAST || $pair[1] ? '' : ' ' . Lang::achievement('slain'), 'proxy' => $pair[2]); if ($pair[2]) { // has proxies assigned, add yourself as another proxy $ol['proxy'][$i] = Util::localizedString($olNPCData->getEntry($i), 'name'); } $this->objectiveList[] = $ol; } } // .. GO interactions if ($ids = array_keys($olGOs)) { $olGOData = new GameObjectList(array(['id', $ids])); $this->extendGlobalData($olGOData->getJSGlobals(GLOBALINFO_SELF)); foreach ($olNPCs as $i => $pair) { if (!$i || !in_array($i, $olGOData->getFoundIDs())) { continue; } $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_OBJECT], 'id' => $i, 'name' => $pair[1] ?: Util::localizedString($olGOData->getEntry($i), 'name'), 'qty' => $pair[0] > 1 ? $pair[0] : 0); } } // reputation required for ($i = 1; $i < 3; $i++) { $id = $this->subject->getField('reqFactionId' . $i); $val = $this->subject->getField('reqFactionValue' . $i); if (!$id) { continue; } $olFactions[$id] = $val; } if ($ids = array_keys($olFactions)) { $olFactionsData = new FactionList(array(['id', $ids])); $this->extendGlobalData($olFactionsData->getJSGlobals(GLOBALINFO_SELF)); foreach ($olFactions as $i => $val) { if (!$i || !in_array($i, $olFactionsData->getFoundIDs())) { continue; } $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_FACTION], 'id' => $i, 'name' => Util::localizedString($olFactionsData->getEntry($i), 'name'), 'qty' => sprintf(Util::$dfnString, $val . ' ' . Lang::achievement('points'), Lang::getReputationLevelForPoints($val)), 'extraText' => ''); } } // granted spell if ($_ = $this->subject->getField('sourceSpellId')) { $this->extendGlobalIds(TYPE_SPELL, $_); $this->objectiveList[] = array('typeStr' => Util::$typeStrings[TYPE_SPELL], 'id' => $_, 'name' => SpellList::getName($_), 'qty' => 0, 'extraText' => ' (' . Lang::quest('provided') . ')'); } // required money if ($this->subject->getField('rewardOrReqMoney') < 0) { $this->objectiveList[] = ['text' => Lang::quest('reqMoney') . Lang::main('colon') . Util::formatMoney(abs($this->subject->getField('rewardOrReqMoney')))]; } // required pvp kills if ($_ = $this->subject->getField('reqPlayerKills')) { $this->objectiveList[] = ['text' => Lang::quest('playerSlain') . ' (' . $_ . ')']; } /**********/ /* Mapper */ /**********/ $this->addJS('?data=zones&locale=' . User::$localeId . '&t=' . $_SESSION['dataKey']); /* TODO (GODDAMNIT): jeez.. */ // $startend + reqNpcOrGo[1-4] $this->map = null; // array( // 'data' => ['zone' => $this->typeId], // 'som' => Util::toJSON($som) // ); /****************/ /* Main Content */ /****************/ $this->gains = $this->createGains(); $this->mail = $this->createMail($maTab, $startEnd); $this->rewards = $this->createRewards(); $this->objectives = $this->subject->parseText('objectives', false); $this->details = $this->subject->parseText('details', false); $this->offerReward = $this->subject->parseText('offerReward', false); $this->requestItems = $this->subject->parseText('requestItems', false); $this->completed = $this->subject->parseText('completed', false); $this->end = $this->subject->parseText('end', false); $this->suggestedPl = $this->subject->getField('suggestedPlayers'); $this->unavailable = $_flags & QUEST_FLAG_UNAVAILABLE || $this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW; $this->redButtons = array(BUTTON_LINKS => ['color' => 'ffffff00', 'linkId' => 'quest:' . $this->typeId . ':' . $_level . ''], BUTTON_WOWHEAD => true); if ($maTab) { $this->lvTabs[] = $maTab; } // factionchange-equivalent if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_quests WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId)) { $altQuest = new QuestList(array(['id', abs($pendant)])); if (!$altQuest->error) { $this->transfer = sprintf(Lang::quest('_transfer'), $altQuest->id, $altQuest->getField('name', true), $pendant > 0 ? 'alliance' : 'horde', $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2)); } } /**************/ /* Extra Tabs */ /**************/ // tab: see also $seeAlso = new QuestList(array(['name_loc' . User::$localeId, '%' . $this->name . '%'], ['id', $this->typeId, '!'])); if (!$seeAlso->error) { $this->extendGlobalData($seeAlso->getJSGlobals()); $this->lvTabs[] = array('file' => 'quest', 'data' => $seeAlso->getListviewData(), 'params' => array('name' => '$LANG.tab_seealso', 'id' => 'see-also')); } // tab: criteria of $criteriaOf = new AchievementList(array(['ac.type', ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST], ['ac.value1', $this->typeId])); if (!$criteriaOf->error) { $this->extendGlobalData($criteriaOf->getJSGlobals()); $this->lvTabs[] = array('file' => 'achievement', 'data' => $criteriaOf->getListviewData(), 'params' => array('name' => '$LANG.tab_criteriaof', 'id' => 'criteria-of')); } // tab: conditions $cnd = []; if ($_ = $this->subject->getField('reqMinRepFaction')) { $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0][] = [CND_REPUTATION_RANK, $_, 1 << Util::getReputationLevelForPoints($this->subject->getField('reqMinRepValue'))]; $this->extendGlobalIds(TYPE_FACTION, $_); } if ($_ = $this->subject->getField('reqMaxRepFaction')) { $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0][] = [-CND_REPUTATION_RANK, $_, 1 << Util::getReputationLevelForPoints($this->subject->getField('reqMaxRepValue'))]; $this->extendGlobalIds(TYPE_FACTION, $_); } $_ = Util::getServerConditions([CND_SRC_QUEST_ACCEPT, CND_SRC_QUEST_SHOW_MARK], null, $this->typeId); if (!empty($_[0])) { // awkward merger if (isset($_[0][CND_SRC_QUEST_ACCEPT][$this->typeId][0])) { if (isset($cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0])) { $cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0] = array_merge($cnd[CND_SRC_QUEST_ACCEPT][$this->typeId][0], $_[0][CND_SRC_QUEST_ACCEPT][$this->typeId][0]); } else { $cnd[CND_SRC_QUEST_ACCEPT] = $_[0][CND_SRC_QUEST_ACCEPT]; } } if (isset($_[0][CND_SRC_QUEST_SHOW_MARK])) { $cnd[CND_SRC_QUEST_SHOW_MARK] = $_[0][CND_SRC_QUEST_SHOW_MARK]; } $this->extendGlobalData($_[1]); } if ($cnd) { $tab = "<script type=\"text/javascript\">\n" . "var markup = ConditionList.createTab(" . Util::toJSON($cnd) . ");\n" . "Markup.printHtml(markup, 'tab-conditions', { allow: Markup.CLASS_STAFF })" . "</script>"; $this->lvTabs[] = array('file' => null, 'data' => $tab, 'params' => array('id' => 'conditions', 'name' => '$LANG.requires')); } }
protected function generateContent() { $_ta = $this->subject->getField('contentGroup'); $_ty = $this->subject->getField('type'); $_cnt = count($this->subject->getField('pieces')); /***********/ /* Infobox */ /***********/ $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); // unavailable (todo (low): set data) if ($this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE) { $infobox[] = Lang::main('unavailable'); } // worldevent if ($e = $this->subject->getField('eventId')) { $infobox[] = Lang::game('eventShort') . Lang::main('colon') . '[event=' . $e . ']'; $this->extendGlobalIds(TYPE_WORLDEVENT, $e); } // itemLevel if ($min = $this->subject->getField('minLevel')) { $foo = Lang::game('level') . Lang::main('colon') . $min; $max = $this->subject->getField('maxLevel'); if ($min < $max) { $foo .= ' - ' . $max; } $infobox[] = $foo; } // class if ($cl = Lang::getClassString($this->subject->getField('classMask'), $jsg, $qty, false)) { $this->extendGlobalIds(TYPE_CLASS, $jsg); $t = $qty == 1 ? Lang::game('class') : Lang::game('classes'); $infobox[] = Util::ucFirst($t) . Lang::main('colon') . $cl; } // required level if ($lvl = $this->subject->getField('reqLevel')) { $infobox[] = sprintf(Lang::game('reqLevel'), $lvl); } // type if ($_ty) { $infobox[] = Lang::game('type') . Lang::main('colon') . Lang::itemset('types', $_ty); } // tag if ($_ta) { $infobox[] = Lang::itemset('_tag') . Lang::main('colon') . '[url=?itemsets&filter=ta=' . $_ta . ']' . Lang::itemset('notes', $_ta) . '[/url]'; } /****************/ /* Main Content */ /****************/ // pieces + Summary $pieces = []; $eqList = []; $compare = []; if (!$this->subject->pieceToSet) { $cnd = [0]; } else { $cnd = ['i.id', array_keys($this->subject->pieceToSet)]; } $iList = new ItemList(array($cnd)); $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON); foreach ($iList->iterate() as $itemId => $__) { if (empty($data[$itemId])) { continue; } $slot = $iList->getField('slot'); $disp = $iList->getField('displayId'); if ($slot && $disp) { $eqList[] = [$slot, $disp]; } $compare[] = $itemId; $pieces[] = array('id' => $itemId, 'name' => $iList->getField('name', true), 'quality' => $iList->getField('quality'), 'icon' => $iList->getField('iconString'), 'json' => $data[$itemId]); } $skill = ''; if ($_sk = $this->subject->getField('skillId')) { $spellLink = sprintf('<a href="?spells=11.%s">%s</a> (%s)', $_sk, Lang::spell('cat', 11, $_sk, 0), $this->subject->getField('skillLevel')); $skill = ' – <small><b>' . sprintf(Lang::game('requires'), $spellLink) . '</b></small>'; } $this->bonusExt = $skill; $this->description = $_ta ? sprintf(Lang::itemset('_desc'), $this->name, Lang::itemset('notes', $_ta), $_cnt) : sprintf(Lang::itemset('_descTagless'), $this->name, $_cnt); $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE; $this->infobox = $infobox ? '[ul][li]' . implode('[/li][li]', $infobox) . '[/li][/ul]' : null; $this->pieces = $pieces; $this->spells = $this->subject->getBonuses(); $this->expansion = 0; $this->redButtons = array(BUTTON_WOWHEAD => $this->typeId > 0, BUTTON_LINKS => ['color' => '', 'linkId' => ''], BUTTON_VIEW3D => ['type' => TYPE_ITEMSET, 'typeId' => $this->typeId, 'equipList' => $eqList], BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt]); $this->compare = array('level' => $this->subject->getField('reqLevel'), 'items' => array_map(function ($v) { return [[$v]]; }, $compare)); /**************/ /* Extra Tabs */ /**************/ // related sets (priority: 1: similar tag + class; 2: has event; 3: no tag + similar type, 4: similar type + profession) $rel = []; if ($_ta && count($this->path) == 3) { $rel[] = ['id', $this->typeId, '!']; $rel[] = ['classMask', 1 << end($this->path) - 1, '&']; $rel[] = ['contentGroup', (int) $_ta]; } else { if ($this->subject->getField('eventId')) { $rel[] = ['id', $this->typeId, '!']; $rel[] = ['eventId', 0, '!']; } else { if ($this->subject->getField('skillId')) { $rel[] = ['id', $this->typeId, '!']; $rel[] = ['contentGroup', 0]; $rel[] = ['skillId', 0, '!']; $rel[] = ['type', $_ty]; } else { if (!$_ta && $_ty) { $rel[] = ['id', $this->typeId, '!']; $rel[] = ['contentGroup', 0]; $rel[] = ['type', $_ty]; $rel[] = ['skillId', 0]; } } } } if ($rel) { $relSets = new ItemsetList($rel); if (!$relSets->error) { $lv = array('file' => 'itemset', 'data' => $relSets->getListviewData(), 'params' => array('id' => 'see-also', 'name' => '$LANG.tab_seealso')); if (!$relSets->hasDiffFields(['classMask'])) { $lv['params']['hiddenCols'] = "\$['classes']"; } $this->lvTabs[] = $lv; $this->extendGlobalData($relSets->getJSGlobals()); } } }