$v) { if (@$str = Util::$itemFilter[$v]) { if ($str == 'rgdspeed') // dont need no duplicate column $str = 'speed'; $wtA[] = '(`ais`.`'.$str.'` * '.intVal($miscData['wtv'][$k]).')'; $wtB[] = '`ais`.`'.$str.'` <> 0'; } } $wtA = $wtA ? '('.implode(' + ', $wtA).')' : 1; $wtB = $wtB ? '('.implode(' AND ', $wtB).')' : 1; $this->setupQuery = $this->weightQuery; $this->matchQuery = $this->weightMatch; $this->setupQuery = strtr($this->setupQuery, ['[weightsA]' => $wtA, '[weightsB]' => $wtB]); $this->matchQuery = strtr($this->matchQuery, ['[weightsA]' => $wtA, '[weightsB]' => $wtB]); } parent::__construct($conditions); while ($this->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 ($miscData && isset($miscData['pcsToSet']) && isset($miscData['pcsToSet'][$this->id])) $this->json[$this->id]['itemset'] = $miscData['pcsToSet'][$this->id]; // unify those pesky masks $_ = $this->curTpl['AllowableClass']; if ($_ < 0 || ($_ & CLASS_MASK_ALL) == CLASS_MASK_ALL) $this->templates[$this->id]['AllowableClass'] = 0; $_ = $this->curTpl['AllowableRace']; if ($_ < 0 || ($_ & RACE_MASK_ALL) == RACE_MASK_ALL) $this->templates[$this->id]['AllowableRace'] = 0; } $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]; // json vs listview quirk $data[$this->id]['name'] = $data[$this->id]['quality'].$data[$this->id]['name']; unset($data[$this->id]['quality']); 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; $_ = $this->curTpl['AllowableRace']; if ($_ && $_ & RACE_MASK_ALLIANCE != RACE_MASK_ALLIANCE && $_ & RACE_MASK_HORDE != RACE_MASK_HORDE) $data[$this->id]['reqrace'] = $_; if ($_ = $this->curTpl['AllowableClass']) $data[$this->id]['reqclass'] = $_; // $data[$this->id]['classes'] ?? if ($this->curTpl['Flags'] & ITEM_FLAG_HEROIC) $data[$this->id]['heroic'] = true; } /* 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 [min, max] // when looting avail unk rel unk glyph major | minor (as id) modelviewer */ return $data; } public function addGlobalsToJscript(&$template, $addMask = 0) { while ($this->iterate()) { $template->extendGlobalData(self::$type, [$this->id => array( 'name' => $this->getField('name', true), '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)) subT (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, $subT = false) { if ($this->error) return; $name = $this->getField('name', true); 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 if (!$subT) $x .= '
'; // name; quality if ($subT) $x .= ''.$name.''; else $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']).Lang::$colon.Util::localizedString($limit, 'name').' ('.$limit['count'].')'; } // max duration if ($this->curTpl['duration'] > 0) $x .= "
".Lang::$game['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 .= '
'.sprintf(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 .= ''; // Class $x .= ''; // Subclass if ($this->curTpl['class'] == ITEM_CLASS_ARMOR && $this->curTpl['subclass'] > 0) $x .= ''; else if ($this->curTpl['class'] == ITEM_CLASS_WEAPON) $x .= ''; else if ($this->curTpl['class'] == ITEM_CLASS_AMMUNITION) $x .= ''; $x .= '
'.Lang::$item['inventoryType'][$this->curTpl['InventoryType']].''.Lang::$item['armorSubClass'][$this->curTpl['subclass']].''.Lang::$item['weaponSubClass'][$this->curTpl['subclass']].''.Lang::$item['projectileSubClass'][$this->curTpl['subclass']].'
'; } else $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 .= ''; $x .= ''; $x .= '
'.sprintf($this->curTpl['dmg_type1'] ? Lang::$item['damageMagic'] : Lang::$item['damagePhys'], $this->curTpl['dmg_min1'].' - '.$this->curTpl['dmg_max1'], Lang::$game['sc'][$this->curTpl['dmg_type1']]).''.Lang::$item['speed'].' '.number_format($speed, 2).'
'; // secondary damage is set if ($this->curTpl['dmg_min2']) $x .= '+'.sprintf($this->curTpl['dmg_type2'] ? Lang::$item['damageMagic'] : Lang::$item['damagePhys'], $this->curTpl['dmg_min2'].' - '.$this->curTpl['dmg_max2'], Lang::$game['sc'][$this->curTpl['dmg_type2']]).'
'; $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 .= ''.sprintf(Lang::$item['armor'], $this->curTpl['armor'] + $this->curTpl['ArmorDamageModifier']).'
'; else if ($this->curTpl['armor']) $x .= ''.sprintf(Lang::$item['armor'], $this->curTpl['armor']).'
'; // Block if ($this->curTpl['block']) $x .= ''.sprintf(Lang::$item['block'], $this->curTpl['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'], STATIC_URL, 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'], STATIC_URL, 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'].Lang::$colon.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'].Lang::$colon.$classes.'
'; // required races if ($races = Lang::getRaceString($this->curTpl['AllowableRace'])) if ($races['side'] != SIDE_BOTH) $x .= Lang::$game['races'].Lang::$colon.$races['name'].'
'; // required honorRank (not used anymore) if ($this->curTpl['requiredhonorrank']) $x .= sprintf(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']) { $_ = ''.SkillList::getName($this->curTpl['RequiredSkill']).''; if ($this->curTpl['RequiredSkillRank']) $_ .= ' ('.$this->curTpl['RequiredSkillRank'].')'; $x .= '
'.sprintf(Lang::$game['requires'], $_); } // required spell if ($this->curTpl['requiredspell']) $x .= '
'.Lang::$game['requires2'].' '.SpellList::getName($this->curTpl['requiredspell']).''; // required reputation w/ faction if ($this->curTpl['RequiredReputationFaction']) $x .= '
'.sprintf(Lang::$game['requires'], 'curTpl['RequiredReputationFaction'].'">'.Faction::getName($this->curTpl['RequiredReputationFaction']).' - '.Lang::$game['rep'][$this->curTpl['RequiredReputationRank']]); // locked if ($lId = $this->curTpl['lockid']) if ($locks = Lang::getLocks($lId)) $x .= '
'.Lang::$item['locked'].'
'.implode('
', $locks).'
'; // upper table: done if (!$subT) $x .= '
'; else $x .= '
'; // spells on item $itemSpellsAndTrigger = []; for ($j = 1; $j <= 5; $j++) if ($this->curTpl['spellid_'.$j] > 0) $itemSpellsAndTrigger[$this->curTpl['spellid_'.$j]] = $this->curTpl['spelltrigger_'.$j]; if ($itemSpellsAndTrigger) { $itemSpells = new SpellList(array(['s.id', array_keys($itemSpellsAndTrigger)])); while ($itemSpells->iterate()) if ($parsed = $itemSpells->parseText('description', $this->curTpl['RequiredLevel'])[0]) $green[] = Lang::$item['trigger'][$itemSpellsAndTrigger[$itemSpells->id]] . ($interactive ? ''.$parsed.'' : $parsed); } // lower table (ratings, spells, ect) if (!$subT) $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 ?_skillline WHERE Id=?d', $itemset['skillId']); $xSet .= '
'.sprintf(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(['s.id', array_keys($setSpellsAndIdx)])); while ($boni->iterate()) { $itemset['spells'][] = array( 'tooltip' => $boni->parseText('description', $this->curTpl['RequiredLevel'])[0], '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(['s.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->getField('name', true).' ('.$reagentItems[$reagents->id].')'; $xCraft .= ''.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'] && !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'].Lang::$colon.Util::formatMoney($this->curTpl['SellPrice']).''; // list required reagents if (isset($xCraft)) $xMisc[] = $xCraft; if ($xMisc) $x .= implode('
', $xMisc); if (!$subT) $x .= '
'; // heirloom tooltip scaling if (isset($this->ssd[$this->id])) { $link = array( $this->id, // itemId 1, // scaleMinLevel $this->ssd[$this->id]['maxLevel'], // scaleMaxLevel $this->ssd[$this->id]['maxLevel'], // scaleCurLevel $this->curTpl['ScalingStatDistribution'], // scaleDist $this->curTpl['ScalingStatValue'], // scaleFlags ); $x .= ''; } else $x .= ''; $this->tooltip[$this->id] = $x; return $this->tooltip[$this->id]; } // from Trinity public function generateEnchSuffixFactor() { $rpp = DB::Aowow()->selectRow('SELECT * FROM ?_itemRandomPropPoints WHERE Id = ?', $this->curTpl['ItemLevel']); if (!$rpp) return 0; switch ($this->curTpl['InventoryType']) { // Items of that type don`t have points case INVTYPE_NON_EQUIP: case INVTYPE_BAG: case INVTYPE_TABARD: case INVTYPE_AMMO: case INVTYPE_QUIVER: case INVTYPE_RELIC: return 0; // Select point coefficient case INVTYPE_HEAD: case INVTYPE_BODY: case INVTYPE_CHEST: case INVTYPE_LEGS: case INVTYPE_2HWEAPON: case INVTYPE_ROBE: $suffixFactor = 1; break; case INVTYPE_SHOULDERS: case INVTYPE_WAIST: case INVTYPE_FEET: case INVTYPE_HANDS: case INVTYPE_TRINKET: $suffixFactor = 2; break; case INVTYPE_NECK: case INVTYPE_WRISTS: case INVTYPE_FINGER: case INVTYPE_SHIELD: case INVTYPE_CLOAK: case INVTYPE_HOLDABLE: $suffixFactor = 3; break; case INVTYPE_WEAPON: case INVTYPE_WEAPONMAINHAND: case INVTYPE_WEAPONOFFHAND: $suffixFactor = 4; break; case INVTYPE_RANGED: case INVTYPE_THROWN: case INVTYPE_RANGEDRIGHT: $suffixFactor = 5; break; default: return 0; } // Select rare/epic modifier switch ($this->curTpl['Quality']) { case ITEM_QUALITY_UNCOMMON: return $rpp['uncommon'.$suffixFactor]; case ITEM_QUALITY_RARE: return $rpp['rare'.$suffixFactor]; case ITEM_QUALITY_EPIC: return $rpp['epic'.$suffixFactor]; case ITEM_QUALITY_LEGENDARY: case ITEM_QUALITY_ARTIFACT: return 0; // not have random properties default: break; } return 0; } public function extendJsonStats() { // convert ItemMods for ($h = 1; $h <= 10; $h++) { $mod = $this->curTpl['stat_type'.$h]; $val = $this->curTpl['stat_value'.$h]; if (!$mod ||!$val) continue; if ($mod == ITEM_MOD_ATTACK_POWER) @$this->itemMods[$this->id][ITEM_MOD_RANGED_ATTACK_POWER] += $val; @$this->itemMods[$this->id][$mod] += $val; } // convert Spells $equipSpells = []; for ($h = 1; $h <= 5; $h++) { // only onEquip if ($this->curTpl['spelltrigger_'.$h] != 1) continue; if ($this->curTpl['spellid_'.$h] <= 0) continue; $equipSpells[] = $this->curTpl['spellid_'.$h]; } if ($equipSpells) { $eqpSplList = new SpellList(array(['s.id', $equipSpells])); $stats = $eqpSplList->getStatGain(); foreach ($stats as $stat) foreach ($stat as $mId => $qty) @$this->itemMods[$this->id][$mId] += $qty; } // fetch and add socketbonusstats if (@$this->json[$this->id]['socketbonus'] > 0) if ($enh = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?;', $this->json[$this->id]['socketbonus'])) $this->json[$this->id]['socketbonusstat'] = Util::parseItemEnchantment($enh); // gather random Enchantments // todo (high): extremly high sql-load if (@$this->json[$this->id]['commondrop'] && isset($this->subItems[$this->id])) { foreach ($this->subItems[$this->id] as $k => $sI) { $jsonEquip = []; $jsonText = []; for ($i = 1; $i < 6; $i++) { if ($sI['enchantId'.$i] <= 0) continue; if (!$this->rndEnchIds[$sI['enchantId'.$i]]) continue; $eData = $this->rndEnchIds[$sI['enchantId'.$i]]; if ($sI['allocationPct'.$i] > 0) // RandomSuffix: scaling Enchantment; enchId < 0 { $amount = intVal($sI['allocationPct'.$i] * $this->generateEnchSuffixFactor() / 10000); $jsonEquip = array_merge($jsonEquip, Util::parseItemEnchantment($eData, $amount)); $jsonText[] = str_replace('$i', $amount, Util::localizedString($eData, 'text')); } else // RandomProperty: static Enchantment; enchId > 0 { $jsonText[] = Util::localizedString($eData, 'text'); $jsonEquip = array_merge($jsonEquip, Util::parseItemEnchantment($eData)); } } $this->subItems[$this->id][$k] = array( 'name' => Util::localizedString($sI, 'name'), 'enchantment' => implode(', ', $jsonText), 'jsonequip' => $jsonEquip ); } $this->json[$this->id]['subitems'] = $this->subItems[$this->id]; } foreach ($this->json[$this->id] as $k => $v) if (!isset($v) || $v === "false" || (!in_array($k, ['classs', 'subclass', 'quality', 'side']) && $v == "0")) unset($this->json[$this->id][$k]); } private function parseRating($type, $value, $interactive = false) { // clamp level range $ssdLvl = isset($this->ssd[$this->id]) ? $this->ssd[$this->id]['maxLevel'] : 1; $level = min(max($this->curTpl['RequiredLevel'], $ssdLvl), MAX_LEVEL); if (!Lang::$item['statType'][$type]) // unknown rating return sprintf(Lang::$item['statType'][count(Lang::$item['statType']) - 1], $type, $value); else if (in_array($type, Util::$lvlIndepRating)) // level independant Bonus return Lang::$item['trigger'][1] . str_replace('%d', ''.$value, Lang::$item['statType'][$type]); else // rating-Bonuses { if ($interactive) $js = ' ('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $type, $value)).'))'; else $js = " (".Util::setRatingLevel($level, $type, $value).")"; return Lang::$item['trigger'][1].str_replace('%d', ''.$value.$js, Lang::$item['statType'][$type]); } } private function getSSDMod($type) { $mask = $this->curTpl['ScalingStatValue']; switch ($type) { case 'stats': $mask &= 0x04001F; break; case 'armor': $mask &= 0xF001E0; break; case 'dps' : $mask &= 0x007E00; break; case 'spell': $mask &= 0x008000; break; default: $mask &= 0x0; } $field = null; for ($i = 0; $i < count(Util::$ssdMaskFields); $i++) if ($mask & (1 << $i)) $field = Util::$ssdMaskFields[$i]; return $field ? DB::Aowow()->selectCell("SELECT ?# FROM ?_scalingstatvalues WHERE charLevel = ?", $field, $this->ssd[$this->id]['maxLevel']) : 0; } private function initScalingStats() { $this->ssd[$this->id] = DB::Aowow()->selectRow("SELECT * FROM ?_scalingstatdistribution WHERE id = ?", $this->curTpl['ScalingStatDistribution']); // stats and ratings for ($i = 1; $i <= 10; $i++) { if ($this->ssd[$this->id]['statMod'.$i] <= 0) { $this->templates[$this->id]['stat_type'.$i] = 0; $this->templates[$this->id]['stat_value'.$i] = 0; } else { $this->templates[$this->id]['stat_type'.$i] = $this->ssd[$this->id]['statMod'.$i]; $this->templates[$this->id]['stat_value'.$i] = intVal(($this->getSSDMod('stats') * $this->ssd[$this->id]['modifier'.$i]) / 10000); } } // armor: only replace if set if ($ssvArmor = $this->getSSDMod('armor')) $this->templates[$this->id]['armor'] = $ssvArmor; // if set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage if ($extraDPS = $this->getSSDMod('dps')) // dmg_x2 not used for heirlooms { $average = $extraDPS * $this->curTpl['delay'] / 1000; $this->templates[$this->id]['dmg_min1'] = number_format(0.7 * $average); $this->templates[$this->id]['dmg_max1'] = number_format(1.3 * $average); } // apply Spell Power from ScalingStatValue if set if ($spellBonus = $this->getSSDMod('spell')) { $this->templates[$this->id]['stat_type10'] = ITEM_MOD_SPELL_POWER; $this->templates[$this->id]['stat_value10'] = $spellBonus; } } private function initSubItems() { $randId = $this->curTpl['RandomProperty'] > 0 ? $this->curTpl['RandomProperty'] : $this->curTpl['RandomSuffix']; if ($randomIds = DB::Aowow()->selectCol('SELECT ench FROM item_enchantment_template WHERE entry = ?d', $randId)) { if ($this->curTpl['RandomSuffix'] > 0) array_walk($randomIds, function($val, $key) use(&$randomIds) { $randomIds[$key] = -$val; }); $this->subItems[$this->id] = DB::Aowow()->select('SELECT *, Id AS ARRAY_KEY FROM ?_itemRandomEnchant WHERE Id IN (?a)', $randomIds); // subitems may share enchantmentIds foreach ($this->subItems[$this->id] as $sI) for ($i = 1; $i < 6; $i++) if (!isset($this->rndEnchIds[$sI['enchantId'.$i]]) && $sI['enchantId'.$i]) if ($enchant = DB::Aowow()->selectRow('SELECT *, Id AS ARRAY_KEY FROM ?_itemenchantment WHERE Id = ?d', $sI['enchantId'.$i])) $this->rndEnchIds[$enchant['id']] = $enchant; } } private function initJsonStats() { $json = array( 'id' => $this->id, 'name' => $this->getField('name', true), 'quality' => ITEM_QUALITY_HEIRLOOM - $this->curTpl['Quality'], 'icon' => $this->curTpl['icon'], 'classs' => $this->curTpl['class'], 'subclass' => $this->curTpl['subclass'], // 'subsubclass' => $this->curTpl['subsubclass'], 'heroic' => (string)($this->curTpl['Flags'] & 0x8), 'side' => Util::sideByRaceMask($this->curTpl['AllowableRace']), // check for FlagsExtra? 0:both; 1: Horde; 2:Alliance 'slot' => $this->curTpl['InventoryType'] == 26 ? 15 : $this->curTpl['InventoryType'] == 20 ? 5 : $this->curTpl['InventoryType'], 'slotbak' => $this->curTpl['InventoryType'], 'level' => $this->curTpl['ItemLevel'], 'reqlevel' => $this->curTpl['RequiredLevel'], 'displayid' => $this->curTpl['displayid'], 'commondrop' => ($this->curTpl['RandomProperty'] > 0 || $this->curTpl['RandomSuffix'] > 0) ? 'true' : null, // string required :( 'holres' => $this->curTpl['holy_res'], 'firres' => $this->curTpl['fire_res'], 'natres' => $this->curTpl['nature_res'], 'frores' => $this->curTpl['frost_res'], 'shares' => $this->curTpl['shadow_res'], 'arcres' => $this->curTpl['arcane_res'], 'armorbonus' => $this->curTpl['ArmorDamageModifier'], 'armor' => $this->curTpl['armor'], 'dura' => $this->curTpl['MaxDurability'], 'itemset' => $this->curTpl['itemset'], 'socket1' => $this->curTpl['socketColor_1'], 'socket2' => $this->curTpl['socketColor_2'], 'socket3' => $this->curTpl['socketColor_3'], 'nsockets' => ($this->curTpl['socketColor_1'] > 0 ? 1 : 0) + ($this->curTpl['socketColor_2'] > 0 ? 1 : 0) + ($this->curTpl['socketColor_3'] > 0 ? 1 : 0), 'socketbonus' => $this->curTpl['socketBonus'], 'scadist' => $this->curTpl['ScalingStatDistribution'], 'scaflags' => $this->curTpl['ScalingStatValue'] ); if ($this->curTpl['class'] == ITEM_CLASS_WEAPON || $this->curTpl['class'] == ITEM_CLASS_AMMUNITION) { $json['dmgtype1'] = $this->curTpl['dmg_type1']; $json['dmgmin1'] = $this->curTpl['dmg_min1'] + $this->curTpl['dmg_min2']; $json['dmgmax1'] = $this->curTpl['dmg_max1'] + $this->curTpl['dmg_max2']; $json['dps'] = !$this->curTpl['delay'] ? 0 : number_format(($json['dmgmin1'] + $json['dmgmax1']) / (2 * $this->curTpl['delay'] / 1000), 1); $json['speed'] = number_format($this->curTpl['delay'] / 1000, 2); if (in_array($json['subclass'], [2, 3, 18, 19])) { $json['rgddmgmin'] = $json['dmgmin1']; $json['rgddmgmax'] = $json['dmgmax1']; $json['rgdspeed'] = $json['speed']; $json['rgddps'] = $json['dps']; } else if ($json['classs'] != ITEM_CLASS_AMMUNITION) { $json['mledmgmin'] = $json['dmgmin1']; $json['mledmgmax'] = $json['dmgmax1']; $json['mlespeed'] = $json['speed']; $json['mledps'] = $json['dps']; } if ($json['classs'] == ITEM_CLASS_WEAPON && in_array($json['subclass'], [5, 6, 10]) && $json['dps'] > 54.8) $json['feratkpwr'] = max(0, round((($json['dmgmin1'] + $json['dmgmax1']) / (2 * $this->curTpl['delay'] / 1000) - 54.8) * 14, 0)); } if ($this->curTpl['ArmorDamageModifier'] > 0) $json['armor'] += $this->curTpl['ArmorDamageModifier']; // clear zero-values afterwards foreach ($json as $k => $v) if (!isset($v) || $v === "false" || (!in_array($k, ['classs', 'subclass', 'quality', 'side']) && $v == "0")) unset($json[$k]); $this->json[$json['id']] = $json; } public function addRewardsToJScript(&$ref) { } } /* teaches $teaches = array(); for($j=1;$j<=4;$j++) if($Row['spellid_'.$j]==483) $teaches[] = spellinfo($Row['spellid_'.($j+1)]); if($teaches) { $item['teaches'] = $teaches; unset($teaches); unset($spellrow); } unlocks $locks_row = $DB->selectCol(' SELECT lockID FROM ?_lock WHERE (type1=1 AND lockproperties1=?d) OR (type2=1 AND lockproperties2=?d) OR (type3=1 AND lockproperties3=?d) OR (type4=1 AND lockproperties4=?d) OR (type5=1 AND lockproperties5=?d) ', $item['entry'], $item['entry'], $item['entry'], $item['entry'], $item['entry'] ); if($locks_row) { // ??????? ??????? ? ????? ????? ?????: $item['unlocks'] = $DB->select(' SELECT ?# FROM gameobject_template WHERE ( ((type IN (?a)) AND (data0 IN (?a))) OR ((type IN (?a)) AND (data0 IN (?a))) ) ', $object_cols[0], array(GAMEOBJECT_TYPE_QUESTGIVER, GAMEOBJECT_TYPE_CHEST, GAMEOBJECT_TYPE_TRAP, GAMEOBJECT_TYPE_GOOBER, GAMEOBJECT_TYPE_CAMERA, GAMEOBJECT_TYPE_FLAGSTAND, GAMEOBJECT_TYPE_FLAGDROP), $locks_row, array(GAMEOBJECT_TYPE_DOOR, GAMEOBJECT_TYPE_BUTTON), $locks_row ); if(!$item['unlocks']) unset($item['unlocks']); */ ?>