['s' => ', 1 as score', 'j' => '?_item_stats AS `is` ON `is`.`id` = `i`.`id`', 'h' => 'score > 0', 'o' => 'score DESC'],
's' => ['j' => ['?_spell AS `s` ON s.effect1CreateItemId = i.id', true], 'g' => 'i.id'],
'i' => ['o' => 'i.quality DESC, i.itemLevel DESC']
);
public function __construct($conditions = [], $applyFilter = false, $miscData = null)
{
// search by statweight
if ($miscData && isset($miscData['wt']) && isset($miscData['wtv']))
$conditions[] = $this->createConditionsForWeights($miscData);
parent::__construct($conditions, $applyFilter);
foreach ($this->iterate() as &$_curTpl)
{
// item is scaling; overwrite other values
if ($_curTpl['scalingStatDistribution'] > 0 && $_curTpl['scalingStatValue'] > 0)
$this->initScalingStats();
$this->initJsonStats();
// readdress itemset .. is wrong for virtual sets
if ($miscData && isset($miscData['pcsToSet']) && isset($miscData['pcsToSet'][$this->id]))
$this->json[$this->id]['itemset'] = $miscData['pcsToSet'][$this->id];
// unify those pesky masks
$_ = &$_curTpl['requiredClass'];
if ($_ < 0 || ($_ & CLASS_MASK_ALL) == CLASS_MASK_ALL)
$_ = 0;
$_ = &$_curTpl['requiredRace'];
if ($_ < 0 || ($_ & RACE_MASK_ALL) == RACE_MASK_ALL)
$_ = 0;
}
}
// use if you JUST need the name
public static function getName($id)
{
$n = DB::Aowow()->selectRow('
SELECT
name_loc0, name_loc2, name_loc3, name_loc6, name_loc8
FROM
?_items
WHERE
id = ?d',
$id
);
return Util::localizedString($n, 'name');
}
// todo (med): information will get lost if one vendor sells one item multiple times with different costs (e.g. for item 54637)
// wowhead seems to have had the same issues
public function getExtendedCost($filter = [], &$reqRating = 0)
{
if (empty($this->vendors))
{
$ids = array_keys($this->templates);
$itemz = DB::Aowow()->select('
SELECT nv.item AS ARRAY_KEY1, nv.entry AS ARRAY_KEY2, 0 AS eventId, nv.maxcount, iec.* FROM npc_vendor nv LEFT JOIN ?_itemextendedcost iec ON nv.extendedCost = iec.id WHERE nv.item IN (?a)
UNION
SELECT genv.item AS ARRAY_KEY1, c.id AS ARRAY_KEY2, genv.eventEntry AS eventId, genv.maxcount, iec.* FROM game_event_npc_vendor genv JOIN creature c ON c.guid = genv.guid LEFT JOIN ?_itemextendedcost iec ON genv.extendedCost = iec.id WHERE genv.item IN (?a)',
$ids,
$ids
);
$cItems = [];
foreach ($itemz as &$vendors)
{
foreach ($vendors as $k => $costs)
{
$data = array(
'stock' => $costs['maxcount'] ? $costs['maxcount'] : -1,
'event' => $costs['eventId'],
'reqRtg' => $costs['reqPersonalRating']
);
if ($_ = $this->getField('buyPrice')) // somewhat nonsense.. is identical for all vendors (obviously)
$data[0] = $_;
// hardcode arena(103) & honor(104)
if ($_ = @$costs['reqArenaPoints'])
{
$data[-103] = $_;
Util::$pageTemplate->extendGlobalIds(TYPE_CURRENCY, 103);
}
if ($_ = @$costs['reqHonorPoints'])
{
$data[-104] = $_;
Util::$pageTemplate->extendGlobalIds(TYPE_CURRENCY, 104);
}
for ($i = 1; $i < 6; $i++)
{
if (($_ = @$costs['reqItemId'.$i]) && $costs['itemCount'.$i] > 0)
{
$data[$_] = $costs['itemCount'.$i];
$cItems[] = $_;
}
}
$vendors[$k] = $data;
}
}
// convert items to currency if possible
if ($cItems)
{
$moneyItems = new CurrencyList(array(['itemId', $cItems]));
$moneyItems->addGlobalsToJscript(Util::$pageTemplate);
foreach ($itemz as $id => &$vendors)
{
foreach ($vendors as &$costs)
{
foreach ($costs as $k => $v)
{
if (in_array($k, $cItems))
{
$found = false;
foreach ($moneyItems->iterate() as $__)
{
if ($moneyItems->getField('itemId') == $k)
{
unset($costs[$k]);
$costs[-$moneyItems->id] = $v;
$found = true;
break;
}
}
if (!$found)
Util::$pageTemplate->extendGlobalIds(TYPE_ITEM, $k);
}
}
}
}
}
$this->vendors = $itemz;
}
$result = $this->vendors;
// apply filter if given
$tok = @$filter[TYPE_ITEM];
$cur = @$filter[TYPE_CURRENCY];
$npc = @$filter[TYPE_NPC]; // bought at specific vendor
$reqRating = -1;
foreach ($result as $itemId => $data)
{
foreach ($data as $npcId => $costs)
{
if ($npc && $npcId != $npc)
{
unset($data[$npcId]);
continue;
}
if ($tok || $cur) // bought with specific token or currency
{
$valid = false;
foreach ($costs as $k => $qty)
{
if ((!$tok || $k == $tok) && (!$cur || $k == -$cur))
{
$valid = true;
break;
}
}
if (!$valid)
unset($data[$npcId]);
}
// reqRating ins't really a cost .. so pass it by ref instead of return
// use lowest total value
// note: how to distinguish between brackets .. or team/pers-rating?
if (isset($data[$npcId]) && ($reqRating > $costs['reqRtg'] || $reqRating < 0 ))
$reqRating = $costs['reqRtg'];
}
if (empty($data))
unset($result[$itemId]);
}
return $result;
}
public function getListviewData($addInfoMask = 0x0, $miscData = null)
{
/*
* ITEMINFO_JSON (0x01): itemMods (including spells) and subitems parsed
* ITEMINFO_SUBITEMS (0x02): searched by comparison
* ITEMINFO_VENDOR (0x04): costs-obj, when displayed as vendor
* ITEMINFO_GEM (0x10): gem infos and score
* ITEMINFO_MODEL (0x20): sameModelAs-Tab
*/
// random item is random
if ($addInfoMask & ITEMINFO_SUBITEMS)
$this->initSubItems();
$data = [];
foreach ($this->iterate() as $__)
{
if ($addInfoMask & ITEMINFO_JSON)
$this->extendJsonStats();
foreach ($this->json[$this->id] as $k => $v)
$data[$this->id][$k] = $v;
// json vs listview quirk
$data[$this->id]['name'] = $data[$this->id]['quality'].$data[$this->id]['name'];
unset($data[$this->id]['quality']);
if ($addInfoMask & ITEMINFO_JSON)
{
foreach ($this->itemMods[$this->id] as $k => $v)
$data[$this->id][Util::$itemMods[$k]] = $v;
if ($_ = intVal(($this->curTpl['minMoneyLoot'] + $this->curTpl['maxMoneyLoot']) / 2))
$data[$this->id]['avgmoney'] = $_;
if ($_ = $this->curTpl['repairPrice'])
$data[$this->id]['repaircost'] = $_;
}
if ($addInfoMask & (ITEMINFO_JSON | ITEMINFO_GEM))
if (isset($this->curTpl['score']))
$data[$this->id]['score'] = $this->curTpl['score'];
if ($addInfoMask & ITEMINFO_GEM)
{
$data[$this->id]['uniqEquip'] = ($this->curTpl['flags'] & ITEM_FLAG_UNIQUEEQUIPPED) ? 1 : 0;
$data[$this->id]['socketLevel'] = 0; // not used with wotlk
}
if ($addInfoMask & ITEMINFO_VENDOR)
{
// just use the first results
// todo (med): dont use first result; search for the right one
$cost = @reset($this->getExtendedCost($miscData)[$this->id]);
if ($cost)
{
$currency = [];
$tokens = [];
foreach ($cost as $k => $qty)
{
if (is_string($k))
continue;
if ($k > 0)
$tokens[] = [$k, $qty];
else if ($k < 0)
$currency[] = [-$k, $qty];
}
$data[$this->id]['stock'] = $cost['stock'];
$data[$this->id]['cost'] = array(
$this->getField('buyPrice'),
$currency ? $currency : null,
$tokens ? $tokens : null
);
}
if ($x = $this->curTpl['buyPrice'])
$data[$this->id]['buyprice'] = $x;
if ($x = $this->curTpl['sellPrice'])
$data[$this->id]['sellprice'] = $x;
if ($x = $this->curTpl['buyCount'])
$data[$this->id]['stack'] = $x;
}
if ($this->curTpl['class'] == ITEM_CLASS_GLYPH)
$data[$this->id]['glyph'] = $this->curTpl['subSubClass'];
if ($x = $this->curTpl['requiredSkill'])
$data[$this->id]['reqskill'] = $x;
if ($x = $this->curTpl['requiredSkillRank'])
$data[$this->id]['reqskillrank'] = $x;
if ($x = $this->curTpl['requiredSpell'])
$data[$this->id]['reqspell'] = $x;
if ($x = $this->curTpl['requiredFaction'])
$data[$this->id]['reqfaction'] = $x;
if ($x = $this->curTpl['requiredFactionRank'])
{
$data[$this->id]['reqrep'] = $x;
$data[$this->id]['standing'] = $x; // used in /faction item-listing
}
if ($x = $this->curTpl['slots'])
$data[$this->id]['nslots'] = $x;
$_ = $this->curTpl['requiredRace'];
if ($_ && $_ & RACE_MASK_ALLIANCE != RACE_MASK_ALLIANCE && $_ & RACE_MASK_HORDE != RACE_MASK_HORDE)
$data[$this->id]['reqrace'] = $_;
if ($_ = $this->curTpl['requiredClass'])
$data[$this->id]['reqclass'] = $_; // $data[$this->id]['classes'] ??
if ($this->curTpl['flags'] & ITEM_FLAG_HEROIC)
$data[$this->id]['heroic'] = true;
if ($addInfoMask & ITEMINFO_MODEL)
if ($_ = $this->getField('displayId'))
$data[$this->id]['displayid'] = $_;
}
/* even more complicated crap
"source":[5],"sourcemore":[{"n":"Commander Oxheart","t":1,"ti":64606,"z":5842}],
avail unk
rel unk
modelviewer {type:X, displayid:Y, slot:z} .. not sure, when to set
*/
return $data;
}
public function addGlobalsToJscript(&$template, $addMask = GLOBALINFO_SELF)
{
foreach ($this->iterate() as $id => $__)
{
$extra = null;
$data = null;
if ($addMask & GLOBALINFO_SELF)
{
$data = array(
$id => array(
'name' => $this->getField('name', true),
'quality' => $this->curTpl['quality'],
'icon' => $this->curTpl['iconString']
)
);
}
if ($addMask & GLOBALINFO_EXTRA)
{
$extra = array(
'id' => $id,
'tooltip' => Util::jsEscape($this->renderTooltip(null, true)),
'spells' => ''
);
}
if ($data || $extra)
$template->extendGlobalData(self::$type, $data, $extra);
}
}
/*
enhance (set by comparison tool or formated external links)
ench: enchantmentId
sock: bool (extraScoket (gloves, belt))
gems: array (:-separated itemIds)
rand: >0: randomPropId; <0: randomSuffixId
interactive (set to place javascript/anchors to manipulate level and ratings or link to filters (static tooltips vs popup tooltip))
subOf (tabled layout doesn't work if used as sub-tooltip in other item or spell tooltips; use line-break instead)
*/
public function renderTooltip($enhance = [], $interactive = false, $subOf = 0)
{
if ($this->error)
return;
if (!empty($this->tooltip[$this->id]))
return $this->tooltip[$this->id];
$_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'];
$causesScaling = false;
if (!empty($enhance['rand']))
{
$rndEnch = DB::Aowow()->selectRow('SELECT * FROM ?_itemrandomenchant WHERE Id = ?d', $enhance['rand']);
$_name .= ' '.Util::localizedString($rndEnch, 'name');
$randEnchant = '';
for ($i = 1; $i < 6; $i++)
{
if ($rndEnch['enchantId'.$i] <= 0)
continue;
$enchant = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?d', $rndEnch['enchantId'.$i]);
if ($rndEnch['allocationPct'.$i] > 0)
{
$amount = intVal($rndEnch['allocationPct'.$i] * $this->generateEnchSuffixFactor());
$randEnchant .= ''.str_replace('$i', $amount, Util::localizedString($enchant, 'text')).'
';
}
else
$randEnchant .= ''.Util::localizedString($enchant, 'text').'
';
}
}
// IMPORTAT: DO NOT REMOVE THE HTML-COMMENTS! THEY ARE REQUIRED TO UPDATE THE TOOLTIP CLIENTSIDE
$x = '';
// upper table: stats
if (!$subOf)
$x .= '
| ';
// name; quality
if ($subOf)
$x .= ''.$_name.'';
else
$x .= ''.$_name.'';
// heroic tag
if (($_flags & ITEM_FLAG_HEROIC) && $_quality == ITEM_QUALITY_EPIC)
$x .= ' '.Lang::$item['heroic'].''; // 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 .= ' '.Util::localizedString($map, 'name').''; } // requires area if ($this->curTpl['area']) { $area = DB::Aowow()->selectRow('SELECT * FROM ?_zones WHERE Id=?d LIMIT 1', $this->curTpl['area']); $x .= ' '.Util::localizedString($area, 'name'); } // conjured if ($_flags & ITEM_FLAG_CONJURED) $x .= ' '.Lang::$item['conjured']; // bonding if ($_flags & ITEM_FLAG_ACCOUNTBOUND) $x .= ' '.Lang::$item['bonding'][0]; else if ($this->curTpl['bonding']) $x .= ' '.Lang::$item['bonding'][$this->curTpl['bonding']]; // unique || unique-equipped || unique-limited if ($this->curTpl['maxCount'] > 0) { $x .= ' '.Lang::$item['unique']; if ($this->curTpl['maxCount'] > 1) $x .= ' ('.$this->curTpl['maxCount'].')'; } else if ($_flags & ITEM_FLAG_UNIQUEEQUIPPED) $x .= ' '.Lang::$item['uniqueEquipped']; else if ($this->curTpl['itemLimitCategory']) { $limit = DB::Aowow()->selectRow("SELECT * FROM ?_itemlimitcategory WHERE id = ?", $this->curTpl['itemLimitCategory']); $x .= ' '.($limit['isGem'] ? Lang::$item['uniqueEquipped'] : Lang::$item['unique']).Lang::$colon.Util::localizedString($limit, 'name').' ('.$limit['count'].')'; } // max duration if ($dur = $this->curTpl['duration']) $x .= " ".Lang::$game['duration'].Lang::$colon.Util::formatTime(abs($dur) * 1000).($this->curTpl['flagsCustom'] & 0x1 ? ' ('.Lang::$item['realTime'].')' : null); // required holiday if ($hId = $this->curTpl['holidayId']) { $hDay = DB::Aowow()->selectRow("SELECT * FROM ?_holidays WHERE id = ?", $hId); $x .= ' '.sprintf(Lang::$game['requires'], ''.Util::localizedString($hDay, 'name').''); } // item begins a quest if ($this->curTpl['startQuest']) $x .= ' '.Lang::$item['startQuest'].''; // containerType (slotCount) if ($this->curTpl['slots'] > 0) { $fam = log($this->curTpl['bagFamily'], 2) + 1; // word order differs <_< if (in_array(User::$localeId, [LOCALE_FR, LOCALE_ES, LOCALE_RU])) $x .= ' '.sprintf(Lang::$item['bagSlotString'], Lang::$item['bagFamily'][$fam], $this->curTpl['slots']); else $x .= ' '.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 .= '
'.Lang::$item['inventoryType'][$this->curTpl['slot']].' '; else $x .= ' '; // 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'].' '; else if ($dps) { if ($_class == ITEM_CLASS_WEAPON) { $x .= '
'; // 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']]).' '; if ($_class == ITEM_CLASS_WEAPON) $x .= '('.number_format($dps, 1).' '.Lang::$item['dps'].') '; // display FeralAttackPower if set if ($fap = $this->getFeralAP()) $x .= '('.$fap.' '.Lang::$item['fap'].') '; } // 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 .= ''.sprintf(Lang::$item['armor'], intVal($this->curTpl['armor'] + $this->curTpl['armorDamageModifier'])).' '; } else if (($this->curTpl['armor'] + $this->curTpl['armorDamageModifier']) > 0) $x .= ''.sprintf(Lang::$item['armor'], intVal($this->curTpl['armor'] + $this->curTpl['armorDamageModifier'])).' '; // Block if ($this->curTpl['block']) $x .= ''.sprintf(Lang::$item['block'], $this->curTpl['block']).' '; // Item is a gem (don't mix with sockets) if ($geId = $this->curTpl['gemEnchantmentId']) { $gemText = DB::Aowow()->selectRow('SELECT * FROM ?_itemEnchantment WHERE id = ?d', $geId); $x .= Util::localizedString($gemText, 'text').' '; } // Random Enchantment - if random enchantment is set, prepend stats from it if ($this->curTpl['randomEnchant'] && !isset($enhance['rand'])) $x .= ''.Lang::$item['randEnchant'].' '; else if (isset($enhance['rand'])) $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 .= ''.($qty > 0 ? '+' : '-').abs($qty).' '.Lang::$item['statType'][$type].' '; 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].' '; // Enchantment if (isset($enhance['ench'])) { $enchText = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?', $enhance['ench']); $x .= ''.Util::localizedString($enchText, 'text').' '; } else // enchantment placeholder $x .= ''; // Sockets w/ Gems if (!empty($enhance['gems'])) { $gems = DB::Aowow()->select(' SELECT i.id AS ARRAY_KEY, i.iconString, ae.*, i.gemColorMask AS colorMask FROM ?_items i JOIN ?_itemenchantment ae ON ae.id = i.gemEnchantmentId WHERE i.id IN (?a)', $enhance['gems'] ); } else $enhance['gems'] = []; // zero fill empty sockets $sockCount = $this->curTpl['socketColor1'] + $this->curTpl['socketColor2'] + $this->curTpl['socketColor3'] + (isset($enhance['sock']) ? 1 : 0); while ($sockCount > count($enhance['gems'])) $enhance['gems'][] = 0; $enhance['gems'] = array_reverse($enhance['gems']); $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['gems']); $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], 'text') : Lang::$item['socket'][$colorId]; if ($interactive) $x .= ''.$text.' '; else $x .= ''.$text.' '; } // fill extra socket if (isset($enhance['sock'])) { $pop = array_pop($enhance['gems']); $col = $pop ? 1 : 0; $icon = $pop ? sprintf(Util::$bgImagePath['tiny'], STATIC_URL, strtolower($gems[$pop]['iconString'])) : null; $text = $pop ? Util::localizedString($gems[$pop], 'text') : Lang::$item['socket'][-1]; if ($interactive) $x .= ''.$text.' '; else $x .= ''.$text.' '; } else // prismatic socket placeholder $x .= ''; if ($this->curTpl['socketBonus']) { $sbonus = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?d', $this->curTpl['socketBonus']); $x .= ''.Lang::$item['socketBonus'].Lang::$colon.Util::localizedString($sbonus, 'text').' '; } // durability if ($dur = $this->curTpl['durability']) $x .= Lang::$item['durability'].' '.$dur.' / '.$dur.' '; // required classes if ($classes = Lang::getClassString($this->curTpl['requiredClass'])) $x .= Lang::$game['classes'].Lang::$colon.$classes.' '; // required races if ($races = Lang::getRaceString($this->curTpl['requiredRace'])) if ($races['name'] != Lang::$game['ra'][0]) // not "both" $x .= Lang::$game['races'].Lang::$colon.$races['name'].' '; // required honorRank (not used anymore) if ($rhr = $this->curTpl['requiredHonorRank']) $x .= sprintf(Lang::$game['requires'], Lang::$game['pvpRank'][$rhr]).' '; // 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) : ''.MAX_LEVEL).')').' '; else if ($_reqLvl > 1) $x .= sprintf(Lang::$game['reqLevel'], $_reqLvl).' '; // required arena team raing / perosnal rating / todo (low): sort out what kind of rating if (@$this->getExtendedCost([], $reqRating)[$this->id] && $reqRating) $x .= sprintf(Lang::$item['reqRating'], $reqRating); // item level if (in_array($_class, [ITEM_CLASS_ARMOR, ITEM_CLASS_WEAPON])) $x .= Lang::$item['itemLevel'].' '.$this->curTpl['itemLevel'].' '; // required skill if ($reqSkill = $this->curTpl['requiredSkill']) { $_ = ''.SkillList::getName($reqSkill).''; if ($this->curTpl['requiredSkillRank'] > 0) $_ .= ' ('.$this->curTpl['requiredSkillRank'].')'; $x .= sprintf(Lang::$game['requires'], $_).' '; } // required spell if ($reqSpell = $this->curTpl['requiredSpell']) $x .= Lang::$game['requires2'].' '.SpellList::getName($reqSpell).' '; // required reputation w/ faction if ($reqFac = $this->curTpl['requiredFaction']) $x .= sprintf(Lang::$game['requires'], ''.FactionList::getName($reqFac).' - '.Lang::$game['rep'][$this->curTpl['requiredFactionRank']]).' '; // locked or openable if ($locks = Lang::getLocks($this->curTpl['lockId'], true)) $x .= ''.Lang::$item['locked'].' '.implode(' ', $locks).' '; else if ($this->curTpl['flags'] & ITEM_FLAG_OPENABLE) $x .= ''.Lang::$item['openClick'].' '; // upper table: done if (!$subOf) $x .= ' |
| ';
if (isset($green))
foreach ($green as $j => $bonus)
if ($bonus)
$x .= ''.$bonus.' '; // Item Set $pieces = []; $condition = ['OR', ['item1', $this->id], ['item2', $this->id], ['item3', $this->id], ['item4', $this->id], ['item5', $this->id], ['item6', $this->id], ['item7', $this->id], ['item8', $this->id], ['item9', $this->id], ['item10', $this->id]]; $itemset = new ItemsetList($condition); if (!$itemset->error) { $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 aowow_items a, aowow_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 = ''.Util::localizedString($p, 'name').''; $xSet = ' '.$itemset->getField('name', true).' (0/'.count($pieces).')'; if ($skId = $itemset->getField('skillId')) // bonus requires skill to activate { $xSet .= ' '.sprintf(Lang::$game['requires'], ''.SkillList::getName($skId).''); if ($_ = $itemset->getField('skillLevel')) $xSet .= ' ('.$_.')'; $xSet .= ' '; } // list pieces $xSet .= ' '.implode(' ', $pieces).' '; // 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 .= ''; 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 .= '('.$setSpells[$i]['bonus'].') '.Lang::$item['set'].': '.$setSpells[$i]['tooltip'].''; if ($i < count($setSpells) - 1) $xSet .= ' '; } $xSet .= ''; } // 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 .= ''.Lang::$item['trigger'][0].' '.$desc.' '; // 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(null, $interactive, $this->id)) $xCraft .= ' '.$itemTT.' '.Lang::$game['requires2'].' '.implode(', ', $reqReag).''; } } } } } // misc (no idea, how to organize the 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[] = '"'.$this->getField('description', true).'"'; // readable if ($this->curTpl['pageTextId']) $xMisc[] = ''.Lang::$item['readClick'].''; // charges (i guess checking first spell is enough (single charges not shown)) if ($this->curTpl['spellCharges1'] > 1 || $this->curTpl['spellCharges1'] < -1) $xMisc[] = ''.abs($this->curTpl['spellCharges1']).' '.Lang::$item['charges'].''; if ($sp = $this->curTpl['sellPrice']) $xMisc[] = ''.Lang::$item['sellPrice'].Lang::$colon.Util::formatMoney($sp).''; // list required reagents if (isset($xCraft)) $xMisc[] = $xCraft; if ($xMisc) $x .= implode(' ', $xMisc); if (!$subOf) $x .= ' |