iterate())
{
// item is scaling; overwrite other values
if ($this->curTpl['ScalingStatDistribution'] > 0 && $this->curTpl['ScalingStatValue'] > 0)
$this->initScalingStats();
$this->initJsonStats();
// readdress itemset .. is wrong for virtual sets
if ($pieceToSet && isset($pieceToSet[$this->id]))
$this->json[$this->id]['itemset'] = $pieceToSet[$this->id];
}
$this->reset(); // restore 'iterator'
}
// use if you JUST need the name
public static function getName($id)
{
$n = DB::Aowow()->selectRow('
SELECT
t.name,
l.*
FROM
item_template t,
locales_item l
WHERE
t.entry = ?d AND
t.entry = l.entry',
$id
);
return Util::localizedString($n, 'name');
}
public static function getEquivalentSetPieces($id)
{
return DB::Aowow()->selectCol('
SELECT
a.entry
FROM
item_template a,
item_template b
WHERE
b.entry = ?d AND
a.InventoryType = b.InventoryType AND
a.itemset = b.itemset',
$id
);
}
// end static use
public function getListviewData($addInfoMask = 0x0)
{
/* looks like this data differs per occasion
*
* maybe split in groups, like:
* ITEMINFO_JSON (0x1): itemMods (including spells) and subitems parsed
* ITEMINFO_SUBITEMS (0x2): searched by comparison
* ITEMINFO_VENDOR (0x4): costs-obj, when displayed as vendor
* ITEMINFO_LOOT (0x8): count, stack, pctstack, modes when displaying loot
*/
$data = [];
while ($this->iterate())
{
// random item is random
if ($this->curTpl['RandomProperty'] > 0 || $this->curTpl['RandomSuffix'] > 0)
if ($addInfoMask & ITEMINFO_SUBITEMS)
$this->initSubItems();
if ($addInfoMask & ITEMINFO_JSON)
$this->extendJsonStats();
$data[$this->id] = $this->json[$this->id];
if (isset($this->itemMods[$this->id])) // due to ITEMINFO_JSON
foreach ($this->itemMods[$this->id] as $k => $v)
$data[$this->id][Util::$itemMods[$k]] = $v;
if ($addInfoMask & ITEMINFO_VENDOR)
{
if ($x = $this->curTpl['BuyPrice'])
$data[$this->id]['buyprice'] = $x;
if ($x = $this->curTpl['SellPrice'])
$data[$this->id]['sellprice'] = $x;
}
// complicated data
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['RequiredReputationFaction'])
$data[$this->id]['reqfaction'] = $x;
if ($x = $this->curTpl['RequiredReputationRank'])
$data[$this->id]['reqrep'] = $x;
if ($x = $this->curTpl['ContainerSlots'])
$data[$this->id]['nslots'] = $x;
if (!in_array($this->curTpl['AllowableRace'], [-1, 0]) && $this->curTpl['AllowableRace'] & RACE_MASK_ALL != RACE_MASK_ALL &&
$this->curTpl['AllowableRace'] & RACE_MASK_ALLIANCE != RACE_MASK_ALLIANCE && $this->curTpl['AllowableRace'] & RACE_MASK_HORDE != RACE_MASK_HORDE)
$data[$this->id]['reqrace'] = $this->curTpl['AllowableRace'];
if (!in_array($this->curTpl['AllowableClass'], [-1, 0]) && $this->curTpl['AllowableClass'] & CLASS_MASK_ALL != CLASS_MASK_ALL)
$data[$this->id]['reqclass'] = $this->curTpl['AllowableClass']; // $data[$this->id]['classes'] ??
}
/* even more complicated crap
"source":[5],
"sourcemore":[{"z":3703}],
{"source":[5],"sourcemore":[{"n":"Commander Oxheart","t":1,"ti":64606,"z":5842}],
cost:[] format unk 0:copper, 1:[items]? 2, 3, 4, 5
stack [unk, unk]
avail unk
rel unk
glyph major | minor (as id)
modelviewer
*/
return $data;
}
public function addGlobalsToJscript(&$refs)
{
if (!isset($refs['gItems']))
$refs['gItems'] = [];
while ($this->iterate())
{
$refs['gItems'][$this->id] = array(
'name' => $this->names[$this->id],
'quality' => $this->curTpl['Quality'],
'icon' => $this->curTpl['icon'],
);
}
}
/*
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))
*/
public function renderTooltip($enhance = [], $interactive = false)
{
if ($this->error)
return;
$name = $this->names[$this->id];
if (!empty($this->tooltip[$this->id]))
return $this->tooltip[$this->id];
if (!empty($enhance['rand']))
{
$rndEnch = DB::Aowow()->selectRow('SELECT * FROM ?_itemrandomenchant WHERE Id = ?d', $enhance['rand']);
$name .= ' '.Util::localizedString($rndEnch, 'name');
$randEnchant['stats'] = '';
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() / 10000);
$randEnchant['stats'] .= ''.str_replace('$i', $amount, Util::localizedString($enchant, 'text')).'
';
}
else
$randEnchant['stats'] .= ''.Util::localizedString($enchant, 'text').'
';
}
}
// IMPORTAT: DO NOT REMOVE THE HTML-COMMENTS! THEY ARE REQUIRED TO UPDATE THE TOOLTIP CLIENTSIDE
$x = '';
// upper table: stats
$x .= '
| ';
// name; quality
$x .= ''.$name.'';
// heroic tag
if (($this->curTpl['Flags'] & ITEM_FLAG_HEROIC) && $this->curTpl['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', $this->curTpl['Map']); $x .= ' '.Util::localizedString($map, 'name'); } // requires area if ($this->curTpl['area']) { $area = DB::Aowow()->selectRow('SELECT * FROM ?_areatable WHERE Id=?d LIMIT 1', $this->curTpl['area']); $x .= ' '.Util::localizedString($area, 'name'); } // conjured if ($this->curTpl['Flags'] & ITEM_FLAG_CONJURED) $x .= ' '.Lang::$game['conjured']; // bonding if (($this->curTpl['Flags'] & ITEM_FLAG_ACCOUNTBOUND) && $this->curTpl['Quality'] == ITEM_QUALITY_HEIRLOOM) $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'] == 1) $x .= ' '.Lang::$item['unique']; else if ($this->curTpl['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']).': '.Util::localizedString($limit, 'name').' ('.$limit['count'].')'; } // max duration if ($this->curTpl['duration'] > 0) $x .= " ".Lang::$item['duration'] . ' '. Util::formatTime($this->curTpl['duration'] * 1000) . ($this->curTpl['duration'] < 0 ? ' ('.Lang::$game['realTime'].')' : null); // required holiday if ($this->curTpl['HolidayId']) { $hDay = DB::Aowow()->selectRow("SELECT * FROM ?_holidays WHERE id = ?", $this->curTpl['HolidayId']); $x .= ' '.Lang::$game['requires'].' '.Util::localizedString($hDay, 'name').''; } // maxcount if ($this->curTpl['maxcount'] > 1) $x .= ' ('.$this->curTpl['maxcount'].')'; // item begins a quest if ($this->curTpl['startquest']) $x .= ' '.Lang::$item['startQuest'].''; // containerType (slotCount) if ($this->curTpl['ContainerSlots'] > 1) { // word order differs <_< if (in_array(User::$localeId, [LOCALE_FR, LOCALE_ES, LOCALE_RU])) $x .= ' '.sprintf(Lang::$item['bagSlotString'], Lang::$item['bagFamily'][$this->curTpl['BagFamily']], $this->curTpl['ContainerSlots']); else $x .= ' '.sprintf(Lang::$item['bagSlotString'], $this->curTpl['ContainerSlots'], Lang::$item['bagFamily'][$this->curTpl['BagFamily']]); } if (in_array($this->curTpl['class'], [ITEM_CLASS_ARMOR, ITEM_CLASS_WEAPON, ITEM_CLASS_AMMUNITION])) { $x .= '
'; // Weapon/Ammunition Stats if (in_array($this->curTpl['class'], [ITEM_CLASS_WEAPON, ITEM_CLASS_AMMUNITION])) { $speed = $this->curTpl['delay'] / 1000; $dmgmin1 = $this->curTpl['dmg_min1'] + $this->curTpl['dmg_min2']; $dmgmax1 = $this->curTpl['dmg_max1'] + $this->curTpl['dmg_max2']; $dps = $speed ? ($dmgmin1 + $dmgmax1) / (2 * $speed) : 0; // regular weapon if ($this->curTpl['class'] == ITEM_CLASS_WEAPON) { $x .= '
'; $x .= '('.number_format($dps, 1).' '.Lang::$item['dps'].') '; // display FeralAttackPower if set if (in_array($this->curTpl['subclass'], [5, 6, 10]) && $dps > 54.8) $x .= '('.round(($dps - 54.8) * 14, 0).' '.Lang::$item['fap'].') '; } // ammunition else $x .= Lang::$item['addsDps'].' '.number_format(($dmgmin1 + $dmgmax1) / 2, 1).' '.Lang::$item['dps2'].' '; } // Armor if ($this->curTpl['class'] == ITEM_CLASS_ARMOR && $this->curTpl['ArmorDamageModifier'] > 0) $x .= ''.($this->curTpl['armor'] + $this->curTpl['ArmorDamageModifier']).' '.Lang::$item['armor'].' '; else if ($this->curTpl['armor']) $x .= ''.$this->curTpl['armor'].' '.Lang::$item['armor'].' '; // Block if ($this->curTpl['block']) $x .= ''.$this->curTpl['block'].' '.Lang::$item['block'].' '; // Item is a gem (don't mix with sockets) if ($this->curTpl['GemProperties']) { $gemText = DB::Aowow()->selectRow('SELECT e.* FROM ?_itemenchantment e, ?_gemproperties p WHERE (p.Id = ?d and e.Id = p.itemenchantmentID)', $this->curTpl['GemProperties']); $x .= Util::localizedString($gemText, 'text').' '; } // Random Enchantment - if random enchantment is set, prepend stats from it if (($this->curTpl['RandomProperty'] || $this->curTpl['RandomSuffix']) && !isset($enhance['rand'])) $x .= ''.Lang::$item['randEnchant'].' '; else if (isset($enhance['rand'])) $x .= $randEnchant['stats']; // itemMods (display stats and save ratings for later use) for ($j = 1; $j <= 10; $j++) { $type = $this->curTpl['stat_type'.$j]; $qty = $this->curTpl['stat_value'.$j]; if (!$qty || $type <= 0) continue; // base stat if ($type >= ITEM_MOD_AGILITY && $type <= ITEM_MOD_STAMINA) $x .= '+'.$qty.' '.Lang::$item['statType'][$type].' '; else // rating with % for reqLevel $green[] = $this->parseRating($type, $qty, $interactive); } // 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 (isset($enhance['gems'])) { $gems = DB::Aowow()->select(' SELECT it.entry AS ARRAY_KEY, ia.icon, ae.*, colorMask FROM item_template it JOIN ?_item_template_addon ia ON ia.id = it.entry JOIN ?_gemproperties ag ON ag.Id = it.GemProperties JOIN ?_itemenchantment ae ON ae.Id = ag.itemEnchantmentID WHERE it.entry IN (?a)', $enhance['gems'] ); } else $enhance['gems'] = []; // zero fill empty sockets $sockCount = $this->curTpl['socketColor_1'] + $this->curTpl['socketColor_2'] + $this->curTpl['socketColor_3'] + (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'], strtolower($gems[$pop]['icon'])) : 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'], strtolower($gems[$pop]['icon'])) : 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'].': '.Util::localizedString($sbonus, 'text').' '; } // durability if ($this->curTpl['MaxDurability']) $x .= Lang::$item['durability'].' '.$this->curTpl['MaxDurability'].' / '.$this->curTpl['MaxDurability'].' '; // required classes if ($classes = Lang::getClassString($this->curTpl['AllowableClass'])) $x .= Lang::$game['classes'].': '.$classes.' '; // required races if ($races = Lang::getRaceString($this->curTpl['AllowableRace'])) $x .= Lang::$game['races'].': '.$races['name'].' '; // required honorRank (not used anymore) if ($this->curTpl['requiredhonorrank']) $x .= Lang::$game['requires'].': '.Lang::$game['pvpRank'][$this->curTpl['requiredhonorrank']].' '; // required CityRank..? // what the f.. // required level if (($this->curTpl['Flags'] & ITEM_FLAG_ACCOUNTBOUND) && $this->curTpl['Quality'] == ITEM_QUALITY_HEIRLOOM) $x .= sprintf(Lang::$game['reqLevelHlm'], ' 1'.Lang::$game['valueDelim'].MAX_LEVEL.' ('.($interactive ? printf(Util::$changeLevelString, MAX_LEVEL) : ''.MAX_LEVEL).')').' '; else if ($this->curTpl['RequiredLevel'] > 1) $x .= sprintf(Lang::$game['reqLevel'], $this->curTpl['RequiredLevel']).' '; // item level $x .= Lang::$item['itemLevel'].' '.$this->curTpl['ItemLevel']; // required skill if ($this->curTpl['RequiredSkill']) { $skillText = DB::Aowow()->selectRow('SELECT * FROM ?_skill WHERE skillID = ?d', $this->curTpl['RequiredSkill']); $x .= ' '.Lang::$game['requires'].' '.Util::localizedString($skillText, 'name').''; if ($this->curTpl['RequiredSkillRank']) $x .= ' ('.$this->curTpl['RequiredSkillRank'].')'; } // required spell if ($this->curTpl['requiredspell']) $x .= ' '.Lang::$game['requires'].' '.SpellList::getName($this->curTpl['requiredspell']).''; // required reputation w/ faction if ($this->curTpl['RequiredReputationFaction']) $x .= ' '.Lang::$game['requires'].' curTpl['RequiredReputationFaction'].'">'.Faction::getName($this->curTpl['RequiredReputationFaction']).' - '.Lang::$game['rep'][$this->curTpl['RequiredReputationRank']]; // locked if ($this->curTpl['lockid']) { $lock = DB::Aowow()->selectRow(' SELECT * FROM ?_lock WHERE lockID = ?d', $this->curTpl['lockid'] ); // only use first useful entry for ($j = 1; $j <= 5; $j++) { if ($lock['type'.$j] == 1) // opened by item { $l = Lang::$game['requires'].' '.Util::getItemName($lock['lockproperties'.$j]).''; break; } else if ($lock['type'.$j] == 2) // opened by skill { $lockText = DB::Aowow()->selectRow('SELECT ?# FROM ?_locktype WHERE id = ?d', $lock['lockproperties'.$j]); $l = Lang::$game['requires'].' '.Util::localizedString($lockText, 'name').' ('.$lock['requiredskill'.$j].')'; break; } } $x .= ' '.Lang::$item['locked'].' '.$l.''; } // upper table: done $x .= ' |
| ';
if (isset($green))
foreach ($green as $j => $bonus)
if ($bonus)
$x .= ''.$bonus.' '; // Item Set $pieces = []; $itemset = DB::Aowow()->selectRow(' SELECT * FROM ?_itemset WHERE (item1=?d or item2=?d or item3=?d or item4=?d or item5=?d or item6=?d or item7=?d or item8=?d or item9=?d or item10=?d)', $this->id, $this->id, $this->id, $this->id, $this->id, $this->id, $this->id, $this->id, $this->id, $this->id ); if ($itemset) { $num = 0; // piece counter for ($i = 1; $i <= 10; $i++) { if ($itemset['item'.$i] <= 0) continue; $num++; $equivalents = ItemList::getEquivalentSetPieces($itemset['item'.$i]); $pieces[] = ''.ItemList::getName($itemset['item'.$i]).''; } $xSet = ' '.Util::localizedString($itemset, 'name').' (0/'.$num.')'; if ($itemset['skillID']) // bonus requires skill to activate { $name = DB::Aowow()->selectRow('SELECT * FROM ?_skill WHERE skillID=?d', $itemset['skillID']); $xSet .= ' '.Lang::$game['requires'].' '.Util::localizedString($name, 'name').''; if ($itemset['skillLevel']) $xSet .= ' ('.$itemset['skillLevel'].')'; $xSet .= ' '; } // list pieces $xSet .= ' '.implode(' ', $pieces).' '; // get bonuses $setSpellsAndIdx = []; for ($j = 1; $j <= 8; $j++) if ($itemset['spell'.$j] > 0) $setSpellsAndIdx[$itemset['spell'.$j]] = $j; // todo: get from static prop? if ($setSpellsAndIdx) { $boni = new SpellList(array(['id', array_keys($setSpellsAndIdx)])); while ($boni->iterate()) { $itemset['spells'][] = array( 'tooltip' => $boni->parseText('description', $this->curTpl['RequiredLevel']), 'entry' => $itemset['spell'.$setSpellsAndIdx[$boni->id]], 'bonus' => $itemset['bonus'.$setSpellsAndIdx[$boni->id]] ); } } // sort and list bonuses $xSet .= ''; for ($i = 0; $i < count($itemset['spells']); $i++) { for ($j = $i; $j < count($itemset['spells']); $j++) { if($itemset['spells'][$j]['bonus'] >= $itemset['spells'][$i]['bonus']) continue; $tmp = $itemset['spells'][$i]; $itemset['spells'][$i] = $itemset['spells'][$j]; $itemset['spells'][$j] = $tmp; } $xSet .= '('.$itemset['spells'][$i]['bonus'].') '.Lang::$item['set'].': '.$itemset['spells'][$i]['tooltip'].''; if ($i < count($itemset['spells']) - 1) $xSet .= ' '; } $xSet .= ''; } // recipe handling (some stray Techniques have subclass == 0), place at bottom of tooltipp if ($this->curTpl['class'] == ITEM_CLASS_RECIPE && ($this->curTpl['subclass'] || $this->curTpl['BagFamily'] == 16)) { $craftSpell = new SpellList(array(['id', (int)$this->curTpl['spellid_2']])); $craftItem = new ItemList(array(['i.entry', (int)$craftSpell->curTpl["effect1CreateItemId"]])); $reagentItems = []; for ($i = 1; $i <= 8; $i++) if ($rId = $craftSpell->getField('reagent'.$i)) $reagentItems[$rId] = $craftSpell->getField('reagentCount'.$i); $reagents = new ItemList(array(['i.entry', array_keys($reagentItems)])); $reqReag = []; $x .= ''.Lang::$item['trigger'][0].' '.Util::localizedString($this->curTpl, 'description').' '; $xCraft = ' '.$craftItem->renderTooltip(null, $interactive).' '; while ($reagents->iterate()) $reqReag[] = ''.$reagents->names[$reagents->id].' ('.$reagentItems[$reagents->id].')'; $xCraft .= ''.Lang::$game['requires']." ".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'] && !isset($xCraft)) $xMisc[] = '"'.Util::localizedString($this->curTpl, 'description').'"'; // readable if ($this->curTpl['PageText']) $xMisc[] = ''.Lang::$item['readClick'].''; // charges (i guess checking first spell is enough (single charges not shown)) if ($this->curTpl['spellcharges_1'] > 1) $xMisc[] = ''.$this->curTpl['spellcharges_1'].' '.Lang::$item['charges'].''; if ($this->curTpl['SellPrice']) $xMisc[] = ''.Lang::$item['sellPrice'].": ".Util::formatMoney($this->curTpl['SellPrice']).''; // list required reagents if (isset($xCraft)) $xMisc[] = $xCraft; if ($xMisc) $x .= implode(' ', $xMisc); $x .= ' |