- do not display serverside events in calendar - include gems in item comparison .. also parse their stats in setup - filters use conditions and are more restrictive - changed DBSimple version so it uses mysqli (mysql is deprecated as of php 5.5) - moved each filter class to matching type; file for baseType and BaseFilter - baseType querys are somewhat modular, trying to avoid ridiculous joins that WILL occur sometimes (especially with items) as far as possible
1793 lines
80 KiB
PHP
1793 lines
80 KiB
PHP
<?php
|
|
|
|
if (!defined('AOWOW_REVISION'))
|
|
die('illegal access');
|
|
|
|
class ItemList extends BaseType
|
|
{
|
|
use ListviewHelper;
|
|
|
|
public static $type = TYPE_ITEM;
|
|
|
|
public $tooltip = '';
|
|
public $json = [];
|
|
public $itemMods = [];
|
|
|
|
public $rndEnchIds = [];
|
|
public $subItems = [];
|
|
|
|
private $ssd = [];
|
|
|
|
protected $queryBase = 'SELECT i.*, i.id AS ARRAY_KEY FROM ?_items i';
|
|
protected $queryOpts = array(
|
|
'is' => [ 's' => ', 1 as score', 'j' => '?_item_stats AS `is` ON `is`.`id` = `i`.`id`', 'h' => 'score > 0', 'o' => 'score DESC'],
|
|
'iet' => [['ire'], 'j' => 'item_enchantment_template AS `iet` ON IF (`ire`.`id` > 0, `iet`.`entry` = `i`.`randomProperty`, `iet`.`entry` = `i`.`randomSuffix`)'],
|
|
'ire' => [['iet'], 'j' => '?_itemrandomenchant AS `ire` ON ABS(ire.id) = iet.ench'],
|
|
'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, $this->queryOpts);
|
|
|
|
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');
|
|
}
|
|
|
|
public static function getEquivalentSetPieces($id)
|
|
{
|
|
return DB::Aowow()->selectCol('
|
|
SELECT
|
|
a.id
|
|
FROM
|
|
?_items a,
|
|
?_items b
|
|
WHERE
|
|
b.id = ?d AND
|
|
a.slotBak = b.slotBak AND
|
|
a.itemset = b.itemset',
|
|
$id
|
|
);
|
|
}
|
|
// end static use
|
|
|
|
public function getDetailPageData()
|
|
{
|
|
$data = array(
|
|
'color' => Util::$rarityColorStings[$this->getField('quality')],
|
|
'icon' => $this->getField('iconString'),
|
|
'name' => $this->getField('name', true),
|
|
'displayId' => $this->getField('displayId'),
|
|
'slot' => $this->getField('slot'),
|
|
'stack' => $this->getField('stackable'),
|
|
'class' => $this->getField('class'),
|
|
);
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function getListviewData($addInfoMask = 0x0)
|
|
{
|
|
/*
|
|
* 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_LOOT (0x08): count, stack, pctstack, modes when displaying loot
|
|
* ITEMINFO_GEM (0x10): gem infos and score
|
|
*/
|
|
|
|
$data = [];
|
|
foreach ($this->iterate() as $__)
|
|
{
|
|
// 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();
|
|
|
|
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 ($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)
|
|
{
|
|
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['requiredFaction'])
|
|
$data[$this->id]['reqfaction'] = $x;
|
|
|
|
if ($x = $this->curTpl['requiredFactionRank'])
|
|
$data[$this->id]['reqrep'] = $x;
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
foreach ($this->iterate() as $__)
|
|
{
|
|
$template->extendGlobalData(self::$type, [$this->id => array(
|
|
'name' => $this->getField('name', true),
|
|
'quality' => $this->curTpl['quality'],
|
|
'icon' => $this->curTpl['iconString']
|
|
)]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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;
|
|
|
|
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'];
|
|
|
|
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'] .= '<span>'.str_replace('$i', $amount, Util::localizedString($enchant, 'text')).'</span><br />';
|
|
}
|
|
else
|
|
$randEnchant['stats'] .= '<span>'.Util::localizedString($enchant, 'text').'</span><br />';
|
|
}
|
|
}
|
|
|
|
// IMPORTAT: DO NOT REMOVE THE HTML-COMMENTS! THEY ARE REQUIRED TO UPDATE THE TOOLTIP CLIENTSIDE
|
|
$x = '';
|
|
|
|
// upper table: stats
|
|
if (!$subT)
|
|
$x .= '<table><tr><td>';
|
|
|
|
// name; quality
|
|
if ($subT)
|
|
$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', $this->curTpl['map']);
|
|
$x .= '<br />'.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 .= '<br />'.Util::localizedString($area, 'name');
|
|
}
|
|
|
|
// conjured
|
|
if ($_flags & ITEM_FLAG_CONJURED)
|
|
$x .= '<br />'.Lang::$item['conjured'];
|
|
|
|
// bonding
|
|
if (($_flags & ITEM_FLAG_ACCOUNTBOUND) && $_quality == ITEM_QUALITY_HEIRLOOM)
|
|
$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'] == 1)
|
|
$x .= '<br />'.Lang::$item['unique'];
|
|
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::$colon.Util::localizedString($limit, 'name').' ('.$limit['count'].')';
|
|
}
|
|
|
|
// max duration
|
|
if ($dur = $this->curTpl['duration'])
|
|
$x .= "<br />".Lang::$game['duration'] . ' '. Util::formatTime(abs($dur) * 1000) . ($dur < 0 ? ' ('.Lang::$game['realTime'].')' : null);
|
|
|
|
// required holiday
|
|
if ($hId = $this->curTpl['holidayId'])
|
|
{
|
|
$hDay = DB::Aowow()->selectRow("SELECT * FROM ?_holidays WHERE id = ?", $hId);
|
|
$x .= '<br />'.sprintf(Lang::$game['requires'], '<a href="'.$hId.'">'.Util::localizedString($hDay, 'name').'</a>');
|
|
}
|
|
|
|
// maxCount
|
|
if ($this->curTpl['maxCount'] > 1)
|
|
$x .= ' ('.$this->curTpl['maxCount'].')';
|
|
|
|
// 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'] > 1)
|
|
{
|
|
$fam = log($this->curTpl['bagFamily'], 2) + 1;
|
|
// 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
|
|
$x .= '<td>'.Lang::$item['inventoryType'][$this->curTpl['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
|
|
$x .= '<br />';
|
|
|
|
// Weapon/Ammunition Stats
|
|
if (in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_AMMUNITION]))
|
|
{
|
|
$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;
|
|
|
|
// regular weapon
|
|
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>';
|
|
$x .= '</tr></table>';
|
|
|
|
// 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 />';
|
|
|
|
$x .= '<!--dps-->('.number_format($dps, 1).' '.Lang::$item['dps'].')<br />';
|
|
|
|
// display FeralAttackPower if set
|
|
if (in_array($_subClass, [5, 6, 10]) && $dps > 54.8)
|
|
$x .= '<span class="c11"><!--fap-->('.round(($dps - 54.8) * 14, 0).' '.Lang::$item['fap'].')</span><br />';
|
|
}
|
|
// ammunition
|
|
else
|
|
$x .= Lang::$item['addsDps'].' '.number_format(($dmgmin1 + $dmgmax1) / 2, 1).' '.Lang::$item['dps2'].'<br />';
|
|
}
|
|
|
|
// Armor
|
|
if ($_class == ITEM_CLASS_ARMOR && ($this->curTpl['armor'] + $this->curTpl['armorDamageModifier']) > 0)
|
|
$x .= '<span class="q2"><!--addamr'.$this->curTpl['armorDamageModifier'].'--><span>'.sprintf(Lang::$item['armor'], $this->curTpl['armor'] + $this->curTpl['armorDamageModifier']).'</span></span><br />';
|
|
else if ($this->curTpl['armor'])
|
|
$x .= '<span><!--amr-->'.sprintf(Lang::$item['armor'], $this->curTpl['armor']).'</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'])
|
|
{
|
|
$gemText = DB::Aowow()->selectRow('SELECT * FROM ?_itemEnchantment WHERE id = ?d', $geId);
|
|
$x .= Util::localizedString($gemText, 'text').'<br />';
|
|
}
|
|
|
|
// Random Enchantment - if random enchantment is set, prepend stats from it
|
|
if (($this->curTpl['randomProperty'] || $this->curTpl['randomSuffix']) && !isset($enhance['rand']))
|
|
$x .= '<span class="q2">'.Lang::$item['randEnchant'].'</span><br />';
|
|
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['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);
|
|
}
|
|
|
|
// 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['ench']))
|
|
{
|
|
$enchText = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE Id = ?', $enhance['ench']);
|
|
$x .= '<span class="q2"><!--e-->'.Util::localizedString($enchText, 'text').'</span><br />';
|
|
}
|
|
else // enchantment placeholder
|
|
$x .= '<!--e-->';
|
|
|
|
// 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']
|
|
);
|
|
echo "ho";
|
|
}
|
|
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 .= '<a href="?items=3&filter=cr=81;crs='.(1 << $colorId).';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['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 .= '<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', $this->curTpl['socketBonus']);
|
|
$x .= '<span class="q'.($hasMatch ? '2' : '0').'">'.Lang::$item['socketBonus'].Lang::$colon.Util::localizedString($sbonus, 'text').'</span><br />';
|
|
}
|
|
|
|
// durability
|
|
if ($dur = $this->curTpl['durability'])
|
|
$x .= Lang::$item['durability'].' '.$dur.' / '.$dur.'<br />';
|
|
|
|
// required classes
|
|
if ($classes = Lang::getClassString($this->curTpl['requiredClass']))
|
|
$x .= Lang::$game['classes'].Lang::$colon.$classes.'<br />';
|
|
|
|
// 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'].'<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 ? printf(Util::$changeLevelString, MAX_LEVEL) : '<!--lvl-->'.MAX_LEVEL).')').'<br />';
|
|
else if ($_reqLvl > 1)
|
|
$x .= sprintf(Lang::$game['reqLevel'], $_reqLvl).'<br />';
|
|
|
|
// item level
|
|
$x .= Lang::$item['itemLevel'].' '.$this->curTpl['itemLevel'];
|
|
|
|
// 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 .= '<br />'.sprintf(Lang::$game['requires'], $_);
|
|
}
|
|
|
|
// required spell
|
|
if ($reqSpell = $this->curTpl['requiredSpell'])
|
|
$x .= '<br />'.Lang::$game['requires2'].' <a class="q1" href="?spell='.$reqSpell.'">'.SpellList::getName($reqSpell).'</a>';
|
|
|
|
// required reputation w/ faction
|
|
if ($reqFac = $this->curTpl['requiredFaction'])
|
|
$x .= '<br />'.sprintf(Lang::$game['requires'], '<a class="q1" href=?faction="'.$reqFac.'">'.FactionList::getName($reqFac).'</a> - '.Lang::$game['rep'][$this->curTpl['requiredFactionRank']]);
|
|
|
|
// locked
|
|
if ($lId = $this->curTpl['lockId'])
|
|
if ($locks = Lang::getLocks($lId))
|
|
$x .= '<br /><span class="q0">'.Lang::$item['locked'].'<br />'.implode('<br />', $locks).'</span>';
|
|
|
|
// upper table: done
|
|
if (!$subT)
|
|
$x .= '</td></tr></table>';
|
|
else
|
|
$x .= '<br>';
|
|
|
|
// 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, true)).')';
|
|
|
|
$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)[0])
|
|
$green[] = Lang::$item['trigger'][$itemSpellsAndTrigger[$itemSpells->id][0]] . ($interactive ? '<a href="?spell='.$itemSpells->id.'">'.$parsed.'</a>' : $parsed) . $itemSpellsAndTrigger[$itemSpells->id][1];
|
|
}
|
|
}
|
|
|
|
// lower table (ratings, spells, ect)
|
|
if (!$subT)
|
|
$x .= '<table><tr><td>';
|
|
|
|
if (isset($green))
|
|
foreach ($green as $j => $bonus)
|
|
if ($bonus)
|
|
$x .= '<span class="q2">'.$bonus.'</span><br />';
|
|
|
|
// 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[] = '<span><!--si'.implode(':', $equivalents).'--><a href="?item='.$itemset['item'.$i].'">'.ItemList::getName($itemset['item'.$i]).'</a></span>';
|
|
}
|
|
|
|
$xSet = '<br /><span class="q"><a href="?itemset='.$itemset['id'].'" class="q">'.Util::localizedString($itemset, 'name').'</a> (0/'.$num.')</span>';
|
|
|
|
if ($itemset['skillId']) // bonus requires skill to activate
|
|
{
|
|
$name = DB::Aowow()->selectRow('SELECT * FROM ?_skillline WHERE Id=?d', $itemset['skillId']);
|
|
$xSet .= '<br />'.sprintf(Lang::$game['requires'], '<a href="?skills='.$itemset['skillId'].'" class="q1">'.Util::localizedString($name, 'name').'</a>');
|
|
|
|
if ($itemset['skillLevel'])
|
|
$xSet .= ' ('.$itemset['skillLevel'].')';
|
|
|
|
$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['spell'.$j] > 0)
|
|
$setSpellsAndIdx[$itemset['spell'.$j]] = $j;
|
|
|
|
// todo: get from static prop?
|
|
if ($setSpellsAndIdx)
|
|
{
|
|
$boni = new SpellList(array(['s.id', array_keys($setSpellsAndIdx)]));
|
|
foreach ($boni->iterate() as $__)
|
|
{
|
|
$itemset['spells'][] = array(
|
|
'tooltip' => $boni->parseText('description', $_reqLvl > 1 ? $_reqLvl : MAX_LEVEL)[0],
|
|
'entry' => $itemset['spell'.$setSpellsAndIdx[$boni->id]],
|
|
'bonus' => $itemset['bonus'.$setSpellsAndIdx[$boni->id]]
|
|
);
|
|
}
|
|
}
|
|
|
|
// sort and list bonuses
|
|
$xSet .= '<span class="q0">';
|
|
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 .= '<span>('.$itemset['spells'][$i]['bonus'].') '.Lang::$item['set'].': <a href="?spell='.$itemset['spells'][$i]['entry'].'">'.$itemset['spells'][$i]['tooltip'].'</a></span>';
|
|
if ($i < count($itemset['spells']) - 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)
|
|
{
|
|
if ($desc = $this->getField('description', true))
|
|
{
|
|
$xCraft = '';
|
|
$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(null, $interactive))
|
|
$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 .= '<br /><span class="q1">'.Lang::$game['requires2'].' '.implode(', ', $reqReag).'</span>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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'] && !isset($xCraft))
|
|
$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)
|
|
$xMisc[] = '<span class="q1">'.$this->curTpl['spellCharges1'].' '.Lang::$item['charges'].'</span>';
|
|
|
|
if ($sp = $this->curTpl['sellPrice'])
|
|
$xMisc[] = '<span class="q1">'.Lang::$item['sellPrice'].Lang::$colon.Util::formatMoney($sp).'</span>';
|
|
|
|
// list required reagents
|
|
if (isset($xCraft))
|
|
$xMisc[] = $xCraft;
|
|
|
|
if ($xMisc)
|
|
$x .= implode('<br />', $xMisc);
|
|
|
|
if (!$subT)
|
|
$x .= '</td></tr></table>';
|
|
|
|
// 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 .= '<!--?'.implode(':', $link).'-->';
|
|
}
|
|
else
|
|
$x .= '<!--?'.$this->id.':1:'.MAX_LEVEL.':'.MAX_LEVEL.'-->';
|
|
|
|
$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['slot'])
|
|
{
|
|
// 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
|
|
$this->itemMods[$this->id] = [];
|
|
for ($h = 1; $h <= 10; $h++)
|
|
{
|
|
$mod = $this->curTpl['statType'.$h];
|
|
$val = $this->curTpl['statValue'.$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);
|
|
|
|
// Item is a gem (don't mix with sockets)
|
|
if ($geId = $this->curTpl['gemEnchantmentId'])
|
|
{
|
|
$gemStats = Util::parseItemEnchantment(DB::Aowow()->selectRow('SELECT * FROM ?_itemEnchantment WHERE id = ?d', $geId));
|
|
|
|
foreach ($gemStats as $mod => $qty)
|
|
@$this->json[$this->id][$mod] += $qty;
|
|
}
|
|
|
|
// 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]);
|
|
}
|
|
|
|
public function createConditionsForWeights(&$data)
|
|
{
|
|
if (count($data['wt']) != count($data['wtv']))
|
|
return null;
|
|
|
|
$select = $cnd = [];
|
|
$wtSum = 0;
|
|
|
|
foreach ($data['wt'] as $k => $v)
|
|
{
|
|
@$str = Util::$itemFilter[$v];
|
|
$qty = intVal($data['wtv'][$k]);
|
|
|
|
if ($str && $qty)
|
|
{
|
|
if ($str == 'rgdspeed') // dont need no duplicate column
|
|
$str = 'speed';
|
|
|
|
if ($str == 'mledps') // todo (med): unify rngdps and mledps to dps
|
|
$str = 'dps';
|
|
|
|
$select[] = '(`is`.`'.$str.'` * '.$qty.')';
|
|
$cnd[] = ['is.'.$str, 0, '>'];
|
|
$wtSum += $qty;
|
|
}
|
|
else // well look at that.. erronous indizes or zero-weights
|
|
{
|
|
unset($data['wt'][$k]);
|
|
unset($data['wtv'][$k]);
|
|
}
|
|
}
|
|
|
|
if (count($cnd) > 1)
|
|
array_unshift($cnd, 'OR');
|
|
else if (count($cnd) == 1)
|
|
$cnd = $cnd[0];
|
|
|
|
$this->queryOpts['is']['s'] = $select ? ', ('.implode(' + ', $select).') / '.$wtSum.' AS score' : null;
|
|
return $cnd;
|
|
}
|
|
|
|
private function canTeachSpell()
|
|
{
|
|
// 483: learn recipe; 55884: learn mount/pet
|
|
if (!in_array($this->curTpl['spellId1'], [483, 55884]))
|
|
return false;
|
|
|
|
if (!$this->curTpl['spellId2'])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private function parseRating($type, $value, $interactive = false)
|
|
{
|
|
// clamp level range
|
|
$ssdLvl = isset($this->ssd[$this->id]) ? $this->ssd[$this->id]['maxLevel'] : 1;
|
|
$reqLvl = $this->curTpl['requiredLevel'] > 1 ? $this->curTpl['requiredLevel'] : MAX_LEVEL;
|
|
$level = min(max($reqLvl, $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', '<!--rtg'.$type.'-->'.$value, Lang::$item['statType'][$type]);
|
|
else // rating-Bonuses
|
|
{
|
|
if ($interactive)
|
|
$js = ' <small>('.sprintf(Util::$changeLevelString, Util::setRatingLevel($level, $type, $value)).')</a>)</small>';
|
|
else
|
|
$js = " <small>(".Util::setRatingLevel($level, $type, $value).")</small>";
|
|
|
|
return Lang::$item['trigger'][1].str_replace('%d', '<!--rtg'.$type.'-->'.$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]['statType'.$i] = 0;
|
|
$this->templates[$this->id]['statValue'.$i] = 0;
|
|
}
|
|
else
|
|
{
|
|
$this->templates[$this->id]['statType'.$i] = $this->ssd[$this->id]['statMod'.$i];
|
|
$this->templates[$this->id]['statValue'.$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]['dmgMin1'] = number_format(0.7 * $average);
|
|
$this->templates[$this->id]['dmgMax1'] = number_format(1.3 * $average);
|
|
}
|
|
|
|
// apply Spell Power from ScalingStatValue if set
|
|
if ($spellBonus = $this->getSSDMod('spell'))
|
|
{
|
|
$this->templates[$this->id]['statType10'] = ITEM_MOD_SPELL_POWER;
|
|
$this->templates[$this->id]['statValue10'] = $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['iconString'],
|
|
'classs' => $this->curTpl['class'],
|
|
'subclass' => $this->curTpl['subClass'],
|
|
'subsubclass' => $this->curTpl['subSubClass'],
|
|
'heroic' => (string)($this->curTpl['flags'] & 0x8),
|
|
'side' => Util::sideByRaceMask($this->curTpl['requiredRace']), // check for FlagsExtra? 0:both; 1: Horde; 2:Alliance
|
|
'slot' => $this->curTpl['slot'],
|
|
'slotbak' => $this->curTpl['slotBak'],
|
|
'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['resHoly'],
|
|
'firres' => $this->curTpl['resFire'],
|
|
'natres' => $this->curTpl['resNature'],
|
|
'frores' => $this->curTpl['resFrost'],
|
|
'shares' => $this->curTpl['resShadow'],
|
|
'arcres' => $this->curTpl['resArcane'],
|
|
'armorbonus' => $this->curTpl['armorDamageModifier'],
|
|
'armor' => $this->curTpl['armor'],
|
|
'dura' => $this->curTpl['durability'],
|
|
'itemset' => $this->curTpl['itemset'],
|
|
'socket1' => $this->curTpl['socketColor1'],
|
|
'socket2' => $this->curTpl['socketColor2'],
|
|
'socket3' => $this->curTpl['socketColor3'],
|
|
'nsockets' => ($this->curTpl['socketColor1'] > 0 ? 1 : 0) + ($this->curTpl['socketColor2'] > 0 ? 1 : 0) + ($this->curTpl['socketColor3'] > 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['dmgType1'];
|
|
$json['dmgmin1'] = $this->curTpl['dmgMin1'] + $this->curTpl['dmgMin2'];
|
|
$json['dmgmax1'] = $this->curTpl['dmgMax1'] + $this->curTpl['dmgMax2'];
|
|
$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) { }
|
|
}
|
|
|
|
|
|
// missing each and everything
|
|
class ItemListFilter extends Filter
|
|
{
|
|
// usable-by - limit weapon/armor selection per CharClass - itemClass => available itemsubclasses
|
|
private $ubFilter = [];
|
|
protected $enums = array(
|
|
99 => array( // profession
|
|
null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773
|
|
),
|
|
66 => array( // profession specialization
|
|
1 => -1,
|
|
2 => [ 9788, 9787, 17041, 17040, 17039 ],
|
|
3 => -1,
|
|
4 => -1,
|
|
5 => [20219, 20222 ],
|
|
6 => -1,
|
|
7 => -1,
|
|
8 => [10656, 10658, 10660 ],
|
|
9 => -1,
|
|
10 => [26798, 26801, 26797 ],
|
|
11 => [ 9788, 9787, 17041, 17040, 17039, 20219, 20222, 10656, 10658, 10660, 26798, 26801, 26797], // i know, i know .. lazy as fuck
|
|
12 => false,
|
|
13 => -1,
|
|
14 => -1,
|
|
15 => -1
|
|
),
|
|
152 => array( // class-specific
|
|
null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, true, false
|
|
),
|
|
153 => array( // race-specific
|
|
null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false
|
|
)
|
|
);
|
|
// cr => [type, field, misc, extraCol]
|
|
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
|
|
146 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_HEROIC ], // heroic
|
|
9 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_CONJURED ], // conjureditem
|
|
83 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_UNIQUEEQUIPPED ], // uniqueequipped
|
|
151 => [FILTER_CR_NUMERIC, 'id', null, true], // id
|
|
100 => [FILTER_CR_NUMERIC, 'is.nsockets' ], // nsockets
|
|
111 => [FILTER_CR_NUMERIC, 'requiredSkillRank', null, true], // reqskillrank
|
|
99 => [FILTER_CR_ENUM, 'requiredSkill' ], // requiresprof
|
|
66 => [FILTER_CR_ENUM, 'requiredSpell' ], // requiresprofspec
|
|
17 => [FILTER_CR_ENUM, 'requiredFaction' ], // requiresrepwith
|
|
169 => [FILTER_CR_ENUM, 'holidayId' ], // requiresevent
|
|
21 => [FILTER_CR_NUMERIC, 'is.agi', null, true], // agi
|
|
23 => [FILTER_CR_NUMERIC, 'is.int', null, true], // int
|
|
22 => [FILTER_CR_NUMERIC, 'is.sta', null, true], // sta
|
|
24 => [FILTER_CR_NUMERIC, 'is.spi', null, true], // spi
|
|
20 => [FILTER_CR_NUMERIC, 'is.str', null, true], // str
|
|
115 => [FILTER_CR_NUMERIC, 'is.health', null, true], // health
|
|
116 => [FILTER_CR_NUMERIC, 'is.mana', null, true], // mana
|
|
60 => [FILTER_CR_NUMERIC, 'is.healthrgn', null, true], // healthrgn
|
|
61 => [FILTER_CR_NUMERIC, 'is.manargn', null, true], // manargn
|
|
41 => [FILTER_CR_NUMERIC, 'is.armor' , null, true], // armor
|
|
44 => [FILTER_CR_NUMERIC, 'is.blockrtng', null, true], // blockrtng
|
|
43 => [FILTER_CR_NUMERIC, 'is.block', null, true], // block
|
|
42 => [FILTER_CR_NUMERIC, 'is.defrtng', null, true], // defrtng
|
|
45 => [FILTER_CR_NUMERIC, 'is.dodgertng', null, true], // dodgertng
|
|
46 => [FILTER_CR_NUMERIC, 'is.parryrtng', null, true], // parryrtng
|
|
79 => [FILTER_CR_NUMERIC, 'is.resirtng', null, true], // resirtng
|
|
77 => [FILTER_CR_NUMERIC, 'is.atkpwr', null, true], // atkpwr
|
|
97 => [FILTER_CR_NUMERIC, 'is.feratkpwr', null, true], // feratkpwr
|
|
114 => [FILTER_CR_NUMERIC, 'is.armorpenrtng', null, true], // armorpenrtng
|
|
96 => [FILTER_CR_NUMERIC, 'is.critstrkrtng', null, true], // critstrkrtng
|
|
117 => [FILTER_CR_NUMERIC, 'is.exprtng', null, true], // exprtng
|
|
103 => [FILTER_CR_NUMERIC, 'is.hastertng', null, true], // hastertng
|
|
119 => [FILTER_CR_NUMERIC, 'is.hitrtng', null, true], // hitrtng
|
|
94 => [FILTER_CR_NUMERIC, 'is.splpen', null, true], // splpen
|
|
123 => [FILTER_CR_NUMERIC, 'is.splpwr', null, true], // splpwr
|
|
52 => [FILTER_CR_NUMERIC, 'is.arcsplpwr', null, true], // arcsplpwr
|
|
53 => [FILTER_CR_NUMERIC, 'is.firsplpwr', null, true], // firsplpwr
|
|
54 => [FILTER_CR_NUMERIC, 'is.frosplpwr', null, true], // frosplpwr
|
|
55 => [FILTER_CR_NUMERIC, 'is.holsplpwr', null, true], // holsplpwr
|
|
56 => [FILTER_CR_NUMERIC, 'is.natsplpwr', null, true], // natsplpwr
|
|
57 => [FILTER_CR_NUMERIC, 'is.shasplpwr', null, true], // shasplpwr
|
|
32 => [FILTER_CR_NUMERIC, 'is.dps', true, true], // dps
|
|
33 => [FILTER_CR_NUMERIC, 'is.dmgmin1', null, true], // dmgmin1
|
|
34 => [FILTER_CR_NUMERIC, 'is.dmgmax1', null, true], // dmgmax1
|
|
36 => [FILTER_CR_NUMERIC, 'is.speed', true, true], // speed
|
|
134 => [FILTER_CR_NUMERIC, 'is.mledps', true, true], // mledps
|
|
135 => [FILTER_CR_NUMERIC, 'is.mledmgmin', null, true], // mledmgmin
|
|
136 => [FILTER_CR_NUMERIC, 'is.mledmgmax', null, true], // mledmgmax
|
|
137 => [FILTER_CR_NUMERIC, 'is.mlespeed', true, true], // mlespeed
|
|
138 => [FILTER_CR_NUMERIC, 'is.rgddps', true, true], // rgddps
|
|
139 => [FILTER_CR_NUMERIC, 'is.rgddmgmin', null, true], // rgddmgmin
|
|
140 => [FILTER_CR_NUMERIC, 'is.rgddmgmax', null, true], // rgddmgmax
|
|
141 => [FILTER_CR_NUMERIC, 'is.rgdspeed', true, true], // rgdspeed
|
|
25 => [FILTER_CR_NUMERIC, 'is.arcres', null, true], // arcres
|
|
26 => [FILTER_CR_NUMERIC, 'is.firres', null, true], // firres
|
|
28 => [FILTER_CR_NUMERIC, 'is.frores', null, true], // frores
|
|
30 => [FILTER_CR_NUMERIC, 'is.holres', null, true], // holres
|
|
27 => [FILTER_CR_NUMERIC, 'is.natres', null, true], // natres
|
|
29 => [FILTER_CR_NUMERIC, 'is.shares', null, true], // shares
|
|
37 => [FILTER_CR_NUMERIC, 'is.mleatkpwr', null, true], // mleatkpwr
|
|
84 => [FILTER_CR_NUMERIC, 'is.mlecritstrkrtng', null, true], // mlecritstrkrtng
|
|
78 => [FILTER_CR_NUMERIC, 'is.mlehastertng', null, true], // mlehastertng
|
|
95 => [FILTER_CR_NUMERIC, 'is.mlehitrtng', null, true], // mlehitrtng
|
|
38 => [FILTER_CR_NUMERIC, 'is.rgdatkpwr', null, true], // rgdatkpwr
|
|
40 => [FILTER_CR_NUMERIC, 'is.rgdcritstrkrtng', null, true], // rgdcritstrkrtng
|
|
101 => [FILTER_CR_NUMERIC, 'is.rgdhastertng', null, true], // rgdhastertng
|
|
39 => [FILTER_CR_NUMERIC, 'is.rgdhitrtng', null, true], // rgdhitrtng
|
|
49 => [FILTER_CR_NUMERIC, 'is.splcritstrkrtng', null, true], // splcritstrkrtng
|
|
102 => [FILTER_CR_NUMERIC, 'is.splhastertng', null, true], // splhastertng
|
|
48 => [FILTER_CR_NUMERIC, 'is.splhitrtng', null, true], // splhitrtng
|
|
51 => [FILTER_CR_NUMERIC, 'is.spldmg', null, true], // spldmg
|
|
50 => [FILTER_CR_NUMERIC, 'is.splheal', null, true], // splheal
|
|
8 => [FILTER_CR_BOOLEAN, 'requiredDisenchantSkill' ], // disenchantable
|
|
59 => [FILTER_CR_NUMERIC, 'durability', null, true], // dura
|
|
104 => [FILTER_CR_STRING, 'description', true ], // flavortext
|
|
7 => [FILTER_CR_BOOLEAN, 'description_loc0', true ], // hasflavortext
|
|
142 => [FILTER_CR_STRING, 'iconString', ], // icon
|
|
|
|
176 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags
|
|
177 => [FILTER_CR_STAFFFLAG, 'flagsExtra' ], // flags2
|
|
);
|
|
|
|
|
|
// $field = Util::$itemFilter[$cr[0]];
|
|
|
|
public function __construct($parent)
|
|
{
|
|
$classes = new CharClassList();
|
|
foreach ($classes->iterate() as $cId => $_tpl)
|
|
{
|
|
// preselect misc subclasses
|
|
$this->ubFilter[$cId] = [ITEM_CLASS_WEAPON => [14], ITEM_CLASS_ARMOR => [0]];
|
|
|
|
for ($i = 0; $i < 21; $i++)
|
|
if ($_tpl['weaponTypeMask'] & (1 << $i))
|
|
$this->ubFilter[$cId][ITEM_CLASS_WEAPON][] = $i;
|
|
|
|
for ($i = 0; $i < 11; $i++)
|
|
if ($_tpl['armorTypeMask'] & (1 << $i))
|
|
$this->ubFilter[$cId][ITEM_CLASS_ARMOR][] = $i;
|
|
}
|
|
|
|
parent::__construct($parent);
|
|
}
|
|
|
|
protected function createSQLForCriterium(&$cr)
|
|
{
|
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
|
{
|
|
if ($genCR = $this->genericCriterion($cr))
|
|
return $genCR;
|
|
|
|
unset($cr);
|
|
$this->error = true;
|
|
return [1];
|
|
}
|
|
|
|
switch ($cr[0])
|
|
{
|
|
case 2: // bindonpickup [yn]
|
|
if ($this->int2Bool($cr[1]))
|
|
return ['bonding', 1, $cr[1] ? null : '!'];
|
|
break;
|
|
case 3: // bindonequip [yn]
|
|
if ($this->int2Bool($cr[1]))
|
|
return ['bonding', 2, $cr[1] ? null : '!'];
|
|
break;
|
|
case 4:
|
|
if ($this->int2Bool($cr[1])) // bindonuse [yn]
|
|
return ['bonding', 3, $cr[1] ? null : '!'];
|
|
break;
|
|
case 168: // teachesspell [yn] 483: learn recipe; 55884: learn mount/pet
|
|
if ($this->int2Bool($cr[1]))
|
|
return ['spellId1', [483, 55884], $cr[1] ? null : '!'];
|
|
break;
|
|
case 15: // unique [yn]
|
|
if ($this->int2Bool($cr[1]))
|
|
return ['maxCount', 1, $cr[1] ? null : '!'];
|
|
break;
|
|
case 80: // has sockets [enum]
|
|
switch ($cr[1])
|
|
{
|
|
case 5: // Yes
|
|
return ['is.nsockets', 0, '!'];
|
|
case 6: // No
|
|
return ['is.nsockets', 0];
|
|
case 1: // Meta
|
|
case 2: // Red
|
|
case 3: // Yellow
|
|
case 4: // Blue
|
|
$mask = 1 << ($cr[1] - 1);
|
|
return ['OR', ['socketColor1', $mask], ['socketColor2', $mask], ['socketColor3', $mask]];
|
|
}
|
|
break;
|
|
case 81: // fits gem slot [enum]
|
|
switch ($cr[1])
|
|
{
|
|
case 5: // Yes
|
|
return ['gemEnchantmentId', 0, '!'];
|
|
case 6: // No
|
|
return ['gemEnchantmentId', 0];
|
|
case 1: // Meta
|
|
case 2: // Red
|
|
case 3: // Yellow
|
|
case 4: // Blue
|
|
$mask = 1 << ($cr[1] - 1);
|
|
return ['AND', ['gemEnchantmentId', 0, '!'], ['gemColorMask', $mask, '&']];
|
|
}
|
|
break;
|
|
case 133: // acc bound [yn]
|
|
if ($this->int2Bool($cr[1]))
|
|
{
|
|
if ($cr[1])
|
|
return ['AND', ['flags', ITEM_FLAG_ACCOUNTBOUND, '&'], ['quality', 7]];
|
|
else
|
|
return ['OR', [['flags', ITEM_FLAG_ACCOUNTBOUND, '&'], 0], ['quality', 7, '!']];
|
|
}
|
|
break;
|
|
case 107: // effecttext [str] not yet parsed ['effectsParsed_loc'.User::$localeId, $cr[2]]
|
|
/* todo */ return [1];
|
|
case 132: // glyphtype [enum] susubclass not yet set
|
|
switch ($cr[1])
|
|
{
|
|
case 1: // Major
|
|
case 2: // Minor
|
|
return ['AND', ['class', 16], ['subSubClass', $cr[1]]];
|
|
}
|
|
break;
|
|
case 124: // randomenchants [str]
|
|
// joining this in one step results in hell .. so .. two steps
|
|
$tmp = DB::Aowow()->selectCol('SELECT IF (ire.id > 0, iet.entry, -iet.entry) FROM item_enchantment_template iet JOIN ?_itemrandomenchant ire ON ABS(ire.id) = iet.ench WHERE ire.name_loc'.User::$localeId.' LIKE ?', '%'.$cr[2].'%');
|
|
$suffix = $prop = [];
|
|
foreach ($tmp as $t)
|
|
{
|
|
if ($t > 0)
|
|
$prop[] = intVal($t);
|
|
else if ($t < 0)
|
|
$suffix[] = -intVal($t);
|
|
}
|
|
|
|
if ($suffix && $prop)
|
|
return ['OR', ['randomProperty', $prop], ['randomSuffix', $suffix]];
|
|
else if ($suffix || $prop)
|
|
return $prop ? ['randomProperty', $prop] : ['randomSuffix', $suffix];
|
|
else
|
|
return [0]; // no results aren't really input errors
|
|
case 125: // reqarenartng [op] [int] JOIN npc_vendor, game_event_npc_vendor, itemextendedcost.dbc
|
|
if (!$this->isSaneNumeric($cr[2]))
|
|
break;
|
|
|
|
$this->formData['extraCols'][] = $cr[0];
|
|
/* todo */ return [1];
|
|
case 160: // relatedevent [enum] like 169 .. crawl though npc_vendor and loot_templates of event-related spawns
|
|
/* todo */ return [1];
|
|
case 152: // classspecific [enum]
|
|
$_ = @$this->enums[$cr[0]][$cr[1]];
|
|
if ($_ !== null)
|
|
{
|
|
if (is_bool($_))
|
|
return $_ ? ['AND', [['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'], ['requiredClass', 0, '>']] : ['OR', [['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL], ['requiredClass', 0]];
|
|
else if (is_int($_))
|
|
return ['AND', [['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'], ['requiredClass', 1 << ($_ - 1), '&']];
|
|
}
|
|
break;
|
|
case 153: // racespecific [enum]
|
|
$_ = @$this->enums[$cr[0]][$cr[1]];
|
|
if ($_ !== null)
|
|
{
|
|
if (is_bool($_))
|
|
return $_ ? ['AND', [['requiredRace', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], ['requiredRace', 0, '>']] : ['OR', [['requiredRace', RACE_MASK_ALL, '&'], RACE_MASK_ALL], ['requiredRace', 0]];
|
|
else if (is_int($_))
|
|
return ['AND', [['requiredRace', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], ['requiredRace', 1 << ($_ - 1), '&']];
|
|
}
|
|
break;
|
|
case 35: // damagetype [enum]
|
|
if (!$this->isSaneNumeric($cr[1]) || $cr[1] > 6 || $cr[1] < 0)
|
|
break;
|
|
|
|
return ['OR', ['dmgType1', $cr[1]], ['dmgType2', $cr[1]]];
|
|
case 109: // armorbonus [op] [int]
|
|
if (!$this->isSaneNumeric($cr[2], false))
|
|
break;
|
|
|
|
$this->formData['extraCols'][] = $cr[0];
|
|
return ['AND', ['armordamagemodifier', $cr[2], $this->int2Op($cr[1])], ['class', ITEM_CLASS_ARMOR]];
|
|
case 86: // craftedprof [profession]
|
|
/* todo */ return [1];
|
|
case 16: // dropsin [zone]
|
|
/* todo */ return [1];
|
|
case 105: // dropsinnormal [heroicdungeon-any]
|
|
/* todo */ return [1];
|
|
case 106: // dropsinheroic [heroicdungeon-any]
|
|
/* todo */ return [1];
|
|
case 147: // dropsinnormal10 [multimoderaid-any]
|
|
/* todo */ return [1];
|
|
case 148: // dropsinnormal25 [multimoderaid-any]
|
|
/* todo */ return [1];
|
|
case 149: // dropsinheroic10 [heroicraid-any]
|
|
/* todo */ return [1];
|
|
case 150: // dropsinheroic25 [heroicraid-any]
|
|
/* todo */ return [1];
|
|
case 68: // otdisenchanting [yn]
|
|
/* todo */ return [1];
|
|
case 69: // otfishing [yn]
|
|
/* todo */ return [1];
|
|
case 70: // otherbgathering [yn]
|
|
/* todo */ return [1];
|
|
case 71: // otitemopening [yn]
|
|
/* todo */ return [1];
|
|
case 72: // otlooting [yn]
|
|
/* todo */ return [1];
|
|
case 143: // otmilling [yn]
|
|
/* todo */ return [1];
|
|
case 73: // otmining [yn]
|
|
/* todo */ return [1];
|
|
case 74: // otobjectopening [yn]
|
|
/* todo */ return [1];
|
|
case 75: // otpickpocketing [yn]
|
|
/* todo */ return [1];
|
|
case 88: // otprospecting [yn]
|
|
/* todo */ return [1];
|
|
case 93: // otpvp [pvp]
|
|
/* todo */ return [1];
|
|
case 171: // otredemption [yn]
|
|
/* todo */ return [1];
|
|
case 76: // otskinning [yn]
|
|
/* todo */ return [1];
|
|
case 158: // purchasablewithcurrency [currency-any]
|
|
/* todo */ return [1];
|
|
case 118: // purchasablewithitem [itemcurrency-any]
|
|
/* todo */ return [1];
|
|
case 144: // purchasablewithhonor [yn]
|
|
/* todo */ return [1];
|
|
case 145: // purchasablewitharena [yn]
|
|
/* todo */ return [1];
|
|
case 18: // rewardedbyfactionquest [side]
|
|
/* todo */ return [1];
|
|
case 126: // rewardedbyquestin [zone-any]
|
|
/* todo */ return [1];
|
|
case 172: // rewardedbyachievement [yn]
|
|
/* todo */ return [1];
|
|
case 92: // soldbyvendor [yn]
|
|
/* todo */ return [1];
|
|
case 129: // soldbynpc [str-small]
|
|
/* todo */ return [1];
|
|
case 161: // availabletoplayers [yn]
|
|
/* todo */ return [1];
|
|
case 90: // avgbuyout [op] [int]
|
|
/* todo */ return [1];
|
|
case 65: // avgmoney [op] [int]
|
|
if (!$this->isSaneNumeric($cr[2]) || $cr[1] < 0)
|
|
break;
|
|
|
|
$this->formData['extraCols'][] = $cr[0];
|
|
return ['AND', ['flags', ITEM_FLAG_OPENABLE, '&'], ['((minMoneyLoot + maxMoneyLoot) / 2)', $cr[2], $this->int2Op($cr[1])]];
|
|
case 62: // cooldown [op] [int] fuck it .. too complex atm
|
|
if (!$this->isSaneNumeric($cr[2]) || $cr[1] < 0)
|
|
break;
|
|
|
|
$this->formData['extraCols'][] = $cr[0];
|
|
/* 5x
|
|
if (id > 0 AND trigger = 0 AND
|
|
(((cd > 0 OR (cat = 0 AND cd = 0)) AND cdMatch) OR
|
|
((cat > 0 OR (cat = 0 AND cd = 0)) AND catMatch))
|
|
)
|
|
*/
|
|
/* todo */ return [1];
|
|
case 162: // deprecated [yn]
|
|
/* todo */ return [1];
|
|
case 163: // disenchantsinto [disenchanting]
|
|
break;
|
|
case 10: // locked [yn]
|
|
break;
|
|
case 159: // millable [yn]
|
|
break;
|
|
case 127: // notavailable [yn]
|
|
break;
|
|
case 85: // objectivequest [side]
|
|
break;
|
|
case 11: // openable [yn]
|
|
break;
|
|
case 12: // partofset [yn]
|
|
break;
|
|
case 98: // partyloot [yn]
|
|
break;
|
|
case 89: // prospectable [yn]
|
|
break;
|
|
case 5: // questitem [yn]
|
|
break;
|
|
case 13: // randomlyenchanted [yn]
|
|
break;
|
|
case 14: // readable [yn]
|
|
break;
|
|
case 87: // reagentforability [profession]
|
|
break;
|
|
case 63: // buyprice [op] [int]
|
|
break;
|
|
case 154: // refundable [yn]
|
|
break;
|
|
case 165: // repaircost [op] [int]
|
|
break;
|
|
case 64: // sellprice [op] [int]
|
|
break;
|
|
case 157: // smartloot [yn]
|
|
break;
|
|
case 6: // startsquest [side]
|
|
break;
|
|
case 91: // tool [totemcategory]
|
|
break;
|
|
case 155: // usableinarenas [yn]
|
|
break;
|
|
case 156: // usablewhenshapeshifted [yn]
|
|
break;
|
|
case 130: // hascomments [yn]
|
|
break;
|
|
case 113: // hasscreenshots [yn]
|
|
break;
|
|
case 167: // hasvideos [yn]
|
|
break;
|
|
}
|
|
|
|
unset($cr);
|
|
$this->error = 1;
|
|
return [1];
|
|
}
|
|
|
|
protected function createSQLForValues()
|
|
{
|
|
$parts = [];
|
|
$_v = $this->fiData['v'];
|
|
|
|
// weights
|
|
if (!empty($_v['wt']) && !empty($_v['wtv']))
|
|
{
|
|
// gm - gem quality (qualityId)
|
|
// jc - jc-gems included (bool)
|
|
|
|
// they MAY be strings if only one weight is set
|
|
$_v['wt'] = (array)$_v['wt'];
|
|
$_v['wtv'] = (array)$_v['wtv'];
|
|
|
|
$parts[] = $this->parent->createConditionsForWeights($_v);
|
|
|
|
foreach ($_v['wt'] as $_)
|
|
$this->formData['extraCols'][] = $_;
|
|
|
|
$this->formData['setWeights'] = [$_v['wt'], $_v['wtv']];
|
|
}
|
|
|
|
// upgrade for [form only]
|
|
if (isset($_v['upg']))
|
|
{
|
|
// valid item?
|
|
if (is_int($_v['upg']) && DB::Aowow()->selectCell('SELECT 1 FROM ?_items WHERE class IN (2, 3, 4) AND id = ?d', $_v['upg']))
|
|
$this->formData['form']['upg'] = $_v['upg'];
|
|
else
|
|
unset($_v['upg']);
|
|
}
|
|
|
|
// group by [form only]
|
|
if (isset($_v['gb']))
|
|
{
|
|
// valid item?
|
|
if (is_int($_v['gb']) && $_v['gb'] >= 0 && $_v['gb'] < 4)
|
|
$this->formData['form']['gb'] = $_v['gb'];
|
|
else
|
|
unset($_v['gb']);
|
|
}
|
|
|
|
// name
|
|
if (isset($_v['na']))
|
|
$parts[] = ['name_loc'.User::$localeId, $_v['na']];
|
|
|
|
// usable-by (not excluded by requiredClass && armor or weapons match mask from ?_classes)
|
|
if (isset($_v['ub']))
|
|
{
|
|
if (in_array($_v['ub'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 11]))
|
|
{
|
|
$parts[] = array(
|
|
'AND',
|
|
['OR', ['requiredClass', 0], ['requiredClass', $this->list2Mask($_v['ub']), '&']],
|
|
[
|
|
'OR',
|
|
['class', [2, 4], '!'],
|
|
['AND', ['class', 2], ['subclassbak', $this->ubFilter[$_v['ub']][ITEM_CLASS_WEAPON]]],
|
|
['AND', ['class', 4], ['subclassbak', $this->ubFilter[$_v['ub']][ITEM_CLASS_ARMOR]]]
|
|
]
|
|
);
|
|
}
|
|
else
|
|
unset($_v['ub']);
|
|
}
|
|
|
|
// quality [list]
|
|
if (isset($_v['qu']))
|
|
{
|
|
$_ = (array)$_v['qu'];
|
|
if (!array_diff($_, array_keys(Util::$rarityColorStings)))
|
|
$parts[] = ['quality', $_];
|
|
else
|
|
unset($_v['qu']);
|
|
}
|
|
|
|
// type
|
|
if (isset($_v['ty']))
|
|
{
|
|
// should be contextual to 'class'
|
|
$_ = (array)$_v['ty'];
|
|
$parts[] = ['subclass', $_];
|
|
}
|
|
|
|
// slot
|
|
if (isset($_v['sl']))
|
|
{
|
|
// should be contextual
|
|
$_ = (array)$_v['sl'];
|
|
$parts[] = ['slot', $_];
|
|
}
|
|
|
|
// side
|
|
if (isset($_v['si']))
|
|
{
|
|
$ex = [['requiredRace', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'];
|
|
$notEx = ['OR', ['requiredRace', 0], [['requiredRace', RACE_MASK_ALL, '&'], RACE_MASK_ALL]];
|
|
|
|
switch ($_v['si'])
|
|
{
|
|
case 3:
|
|
$parts[] = $notEx;
|
|
break;
|
|
case 2:
|
|
$parts[] = ['OR', $notEx, ['requiredRace', RACE_MASK_HORDE, '&']];
|
|
break;
|
|
case -2:
|
|
$parts[] = ['AND', $ex, ['requiredRace', RACE_MASK_HORDE, '&']];
|
|
break;
|
|
case 1:
|
|
$parts[] = ['OR', $notEx, ['requiredRace', RACE_MASK_ALLIANCE, '&']];
|
|
break;
|
|
case -1:
|
|
$parts[] = ['AND', $ex, ['requiredRace', RACE_MASK_ALLIANCE, '&']];
|
|
break;
|
|
default:
|
|
unset($_v['si']);
|
|
}
|
|
}
|
|
|
|
// itemLevel min
|
|
if (isset($_v['minle']))
|
|
{
|
|
if (is_int($_v['minle']) && $_v['minle'] > 0)
|
|
$parts[] = ['itemLevel', $_v['minle'], '>='];
|
|
else
|
|
unset($_v['minle']);
|
|
}
|
|
|
|
// itemLevel max
|
|
if (isset($_v['maxle']))
|
|
{
|
|
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
|
|
$parts[] = ['itemLevel', $_v['maxle'], '<='];
|
|
else
|
|
unset($_v['maxle']);
|
|
}
|
|
|
|
// reqLevel min
|
|
if (isset($_v['minrl']))
|
|
{
|
|
if (is_int($_v['minrl']) && $_v['minrl'] > 0)
|
|
$parts[] = ['requiredLevel', $_v['minrl'], '>='];
|
|
else
|
|
unset($_v['minrl']);
|
|
}
|
|
|
|
// reqLevel max
|
|
if (isset($_v['maxrl']))
|
|
{
|
|
if (is_int($_v['maxrl']) && $_v['maxrl'] > 0)
|
|
$parts[] = ['requiredLevel', $_v['maxrl'], '<='];
|
|
else
|
|
unset($_v['maxrl']);
|
|
}
|
|
|
|
return $parts;
|
|
}
|
|
}
|
|
|
|
?>
|